From b214cfb4b98be9464ff9f5a052a26fbbece52325 Mon Sep 17 00:00:00 2001 From: Julian Simioni Date: Wed, 29 Oct 2014 18:54:35 -0700 Subject: [PATCH 001/585] Fix unused variable warnings --- tiny_obj_loader.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 75f0dca5..b73e7662 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -238,10 +238,6 @@ exportFaceGroupToShape( return false; } - size_t offset; - - offset = shape.mesh.indices.size(); - // Flatten vertices and indices for (size_t i = 0; i < faceGroup.size(); i++) { const std::vector& face = faceGroup[i]; @@ -624,7 +620,6 @@ std::string LoadObj( token += 7; sscanf(token, "%s", namebuf); - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, false); faceGroup.clear(); if (material_map.find(namebuf) != material_map.end()) { From 80b243092b5cb3c3523030a188bd50aeaf8fd1e4 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 14 Nov 2014 14:32:20 +0100 Subject: [PATCH 002/585] base of python module --- python/howto.py | 5 ++ python/main.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++ python/pyTOL.cbp.mak | 96 +++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 python/howto.py create mode 100644 python/main.cpp create mode 100644 python/pyTOL.cbp.mak diff --git a/python/howto.py b/python/howto.py new file mode 100644 index 00000000..1342dacb --- /dev/null +++ b/python/howto.py @@ -0,0 +1,5 @@ +import tinyobjloader as tol + +model = tol.LoadObj("cube.obj") + +print(model["shapes"], model["materials"]) diff --git a/python/main.cpp b/python/main.cpp new file mode 100644 index 00000000..e41916cd --- /dev/null +++ b/python/main.cpp @@ -0,0 +1,144 @@ +//python3 module for tinyobjloader +// +//usage: +// import tinyobjloader as tol +// model = tol.LoadObj(name) +// print(model["shapes"]) +// print(model["materials"] +#include +#include +#include "tiny_obj_loader.h" + +typedef std::vector vectd; + +PyObject* +pyTupleFromfloat3 (float array[3]) +{ + int i; + PyObject* tuple = PyTuple_New(3); + + for(i=0; i<=2 ; i++){ + PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i])); + } + + return tuple; +} + +extern "C" +{ + +static PyObject* +pyLoadObj(PyObject* self, PyObject* args) +{ + PyObject *rtntpl, *pyshapes, *pymaterials; + char const* filename; + std::vector shapes; + std::vector materials; + + if(!PyArg_ParseTuple(args, "s", &filename)) + return NULL; + + tinyobj::LoadObj(shapes, materials, filename); + + pyshapes = PyDict_New(); + pymaterials = PyDict_New(); + rtntpl = PyDict_New(); + + for (std::vector::iterator shape = shapes.begin() ; + shape != shapes.end(); shape++) + { + PyObject *meshobj; + PyObject *positions, *normals, *texcoords, *indices, *material_ids; + + meshobj = PyTuple_New(5); + positions = PyList_New(0); + normals = PyList_New(0); + texcoords = PyList_New(0); + indices = PyList_New(0); + material_ids = PyList_New(0); + + tinyobj::mesh_t cm = (*shape).mesh; + for (int i = 0; i <= 4 ; i++ ) + { + PyObject *current; + vectd vect; + + switch (i) + { + case 0: current = positions; + vect = vectd(cm.positions.begin(), cm.positions.end()); + case 1: current = normals; + vect = vectd(cm.normals.begin(), cm.normals.end()); + case 2: current = texcoords; + vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); + case 3: current = indices; + vect = vectd(cm.indices.begin(), cm.indices.end()); + case 4: current = material_ids; + vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); + } + + for (std::vector::iterator it = vect.begin() ; + it != vect.end(); it++) + { + PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it)); + } + + PyTuple_SetItem(meshobj, i, current); + } + + PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj); + } + + for (std::vector::iterator mat = materials.begin() ; + mat != materials.end(); mat++) + { + PyObject *matobj = PyDict_New(); + + PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess)); + PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior)); + PyDict_SetItemString(matobj, "dissolve", PyFloat_FromDouble((*mat).dissolve)); + PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum)); + PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str())); + PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str())); + PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str())); + PyDict_SetItemString(matobj, "normal_texname", PyUnicode_FromString((*mat).normal_texname.c_str())); + PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient)); + PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse)); + PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular)); + PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance)); + PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission)); + + PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj); + } + + PyDict_SetItemString(rtntpl, "shapes", pyshapes); + PyDict_SetItemString(rtntpl, "materials", pymaterials); + + return rtntpl; +} + + +static PyMethodDef mMethods[] = { + +{"LoadObj", pyLoadObj, METH_VARARGS}, +{NULL, NULL, 0, NULL} + +}; + + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "tinyobjloader", + NULL, + -1, + mMethods +}; + + +PyMODINIT_FUNC +PyInit_tinyobjloader(void) +{ + return PyModule_Create(&moduledef); +} + +} diff --git a/python/pyTOL.cbp.mak b/python/pyTOL.cbp.mak new file mode 100644 index 00000000..b14db6e0 --- /dev/null +++ b/python/pyTOL.cbp.mak @@ -0,0 +1,96 @@ +#------------------------------------------------------------------------------# +# This makefile was generated by 'cbp2make' tool rev.147 # +#------------------------------------------------------------------------------# +#requirements : Python 3 (- dev) + +WORKDIR = `pwd` + +CC = gcc +CXX = g++ +AR = ar +LD = g++ +WINDRES = windres + +INC = +CFLAGS = -Wall -fexceptions `python3-config --cflags` +RESINC = +LIBDIR = +LIB = +LDFLAGS = `python3-config --ldflags` + +INC_DEBUG = $(INC) +CFLAGS_DEBUG = $(CFLAGS) -g +RESINC_DEBUG = $(RESINC) +RCFLAGS_DEBUG = $(RCFLAGS) +LIBDIR_DEBUG = $(LIBDIR) +LIB_DEBUG = $(LIB) +LDFLAGS_DEBUG = $(LDFLAGS) +OBJDIR_DEBUG = obj/Debug +DEP_DEBUG = +OUT_DEBUG = bin/Debug/tinyobjloader.so + +INC_RELEASE = $(INC) +CFLAGS_RELEASE = $(CFLAGS) -O2 +RESINC_RELEASE = $(RESINC) +RCFLAGS_RELEASE = $(RCFLAGS) +LIBDIR_RELEASE = $(LIBDIR) +LIB_RELEASE = $(LIB) +LDFLAGS_RELEASE = $(LDFLAGS) -s +OBJDIR_RELEASE = obj/Release +DEP_RELEASE = +OUT_RELEASE = bin/Release/tinyobjloader.so + +OBJ_DEBUG = $(OBJDIR_DEBUG)/main.o $(OBJDIR_DEBUG)/tiny_obj_loader.o + +OBJ_RELEASE = $(OBJDIR_RELEASE)/main.o $(OBJDIR_RELEASE)/tiny_obj_loader.o + +all: debug release + +clean: clean_debug clean_release + +before_debug: + test -d bin/Debug || mkdir -p bin/Debug + test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG) + +after_debug: + +debug: before_debug out_debug after_debug + +out_debug: before_debug $(OBJ_DEBUG) $(DEP_DEBUG) + $(LD) -shared $(LIBDIR_DEBUG) $(OBJ_DEBUG) -o $(OUT_DEBUG) $(LDFLAGS_DEBUG) $(LIB_DEBUG) + +$(OBJDIR_DEBUG)/main.o: main.cpp + $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c main.cpp -o $(OBJDIR_DEBUG)/main.o + +$(OBJDIR_DEBUG)/tiny_obj_loader.o: tiny_obj_loader.cc + $(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c tiny_obj_loader.cc -o $(OBJDIR_DEBUG)/tiny_obj_loader.o + +clean_debug: + rm -f $(OBJ_DEBUG) $(OUT_DEBUG) + rm -rf bin/Debug + rm -rf $(OBJDIR_DEBUG) + +before_release: + test -d bin/Release || mkdir -p bin/Release + test -d $(OBJDIR_RELEASE) || mkdir -p $(OBJDIR_RELEASE) + +after_release: + +release: before_release out_release after_release + +out_release: before_release $(OBJ_RELEASE) $(DEP_RELEASE) + $(LD) -shared $(LIBDIR_RELEASE) $(OBJ_RELEASE) -o $(OUT_RELEASE) $(LDFLAGS_RELEASE) $(LIB_RELEASE) + +$(OBJDIR_RELEASE)/main.o: main.cpp + $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c main.cpp -o $(OBJDIR_RELEASE)/main.o + +$(OBJDIR_RELEASE)/tiny_obj_loader.o: tiny_obj_loader.cc + $(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c tiny_obj_loader.cc -o $(OBJDIR_RELEASE)/tiny_obj_loader.o + +clean_release: + rm -f $(OBJ_RELEASE) $(OUT_RELEASE) + rm -rf bin/Release + rm -rf $(OBJDIR_RELEASE) + +.PHONY: before_debug after_debug clean_debug before_release after_release clean_release + From 878a6560cd6b9f9eff32e12df862b2726fb3db74 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 14 Nov 2014 15:22:50 +0100 Subject: [PATCH 003/585] fix makefile --- python/main.cpp | 2 +- python/pyTOL.cbp.mak | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/main.cpp b/python/main.cpp index e41916cd..636a24d5 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -7,7 +7,7 @@ // print(model["materials"] #include #include -#include "tiny_obj_loader.h" +#include "../tiny_obj_loader.h" typedef std::vector vectd; diff --git a/python/pyTOL.cbp.mak b/python/pyTOL.cbp.mak index b14db6e0..ba18ae99 100644 --- a/python/pyTOL.cbp.mak +++ b/python/pyTOL.cbp.mak @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------# # This makefile was generated by 'cbp2make' tool rev.147 # #------------------------------------------------------------------------------# -#requirements : Python 3 (- dev) + WORKDIR = `pwd` @@ -62,8 +62,8 @@ out_debug: before_debug $(OBJ_DEBUG) $(DEP_DEBUG) $(OBJDIR_DEBUG)/main.o: main.cpp $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c main.cpp -o $(OBJDIR_DEBUG)/main.o -$(OBJDIR_DEBUG)/tiny_obj_loader.o: tiny_obj_loader.cc - $(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c tiny_obj_loader.cc -o $(OBJDIR_DEBUG)/tiny_obj_loader.o +$(OBJDIR_DEBUG)/tiny_obj_loader.o: ../tiny_obj_loader.cc + $(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c ../tiny_obj_loader.cc -o $(OBJDIR_DEBUG)/tiny_obj_loader.o clean_debug: rm -f $(OBJ_DEBUG) $(OUT_DEBUG) @@ -84,8 +84,8 @@ out_release: before_release $(OBJ_RELEASE) $(DEP_RELEASE) $(OBJDIR_RELEASE)/main.o: main.cpp $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c main.cpp -o $(OBJDIR_RELEASE)/main.o -$(OBJDIR_RELEASE)/tiny_obj_loader.o: tiny_obj_loader.cc - $(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c tiny_obj_loader.cc -o $(OBJDIR_RELEASE)/tiny_obj_loader.o +$(OBJDIR_RELEASE)/tiny_obj_loader.o: ../tiny_obj_loader.cc + $(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c ../tiny_obj_loader.cc -o $(OBJDIR_RELEASE)/tiny_obj_loader.o clean_release: rm -f $(OBJ_RELEASE) $(OUT_RELEASE) From f750f3faebb688a0c7f3d918fb29a96c2ec652e4 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 16 Nov 2014 19:35:18 +0100 Subject: [PATCH 004/585] adding setup.py file --- python/setup.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/setup.py diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 00000000..de7b9766 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,13 @@ +from distutils.core import setup, Extension + + +m = Extension('tinyobjloader', + sources = ['main.cpp', '../tiny_obj_loader.cc']) + + +setup (name = 'tinyobjloader', + version = '0.1', + description = 'Python module for tinyobjloader', + ext_modules = [m]) + + From e5bbda3835e2e848c951a2aa052847864b956b7d Mon Sep 17 00:00:00 2001 From: Ododo Date: Sun, 16 Nov 2014 21:08:44 +0100 Subject: [PATCH 005/585] Update howto.py --- python/howto.py | 560 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 559 insertions(+), 1 deletion(-) diff --git a/python/howto.py b/python/howto.py index 1342dacb..099a1af7 100644 --- a/python/howto.py +++ b/python/howto.py @@ -1,5 +1,563 @@ import tinyobjloader as tol +import json model = tol.LoadObj("cube.obj") -print(model["shapes"], model["materials"]) +#print(model["shapes"], model["materials"]) +print( json.dumps(model, indent=4) ) + +#EXAMPLE OUTPUT + +##{ +## "shapes": { +## "left": [ +## [ +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0 +## ], +## [ +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0 +## ], +## [ +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0 +## ], +## [ +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0 +## ], +## [ +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0, +## 2.0 +## ] +## ], +## "bottom": [ +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ] +## ], +## "right": [ +## [ +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0 +## ], +## [ +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0 +## ], +## [ +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0 +## ], +## [ +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0 +## ], +## [ +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0, +## 1.0 +## ] +## ], +## "front": [ +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ] +## ], +## "top": [ +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ] +## ], +## "back": [ +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ], +## [ +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0, +## 0.0 +## ] +## ] +## }, +## "materials": { +## "green": { +## "emission": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "specular": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "illum": 0, +## "ior": 1.0, +## "shininess": 1.0, +## "normal_texname": "", +## "specular_texname": "", +## "transmittance": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "dissolve": 1.0, +## "ambient": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "diffuse": [ +## 0.0, +## 1.0, +## 0.0 +## ], +## "diffuse_texname": "", +## "ambient_texname": "" +## }, +## "blue": { +## "emission": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "specular": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "illum": 0, +## "ior": 1.0, +## "shininess": 1.0, +## "normal_texname": "", +## "specular_texname": "", +## "transmittance": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "dissolve": 1.0, +## "ambient": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "diffuse": [ +## 0.0, +## 0.0, +## 1.0 +## ], +## "diffuse_texname": "", +## "ambient_texname": "" +## }, +## "red": { +## "emission": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "specular": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "illum": 0, +## "ior": 1.0, +## "shininess": 1.0, +## "normal_texname": "", +## "specular_texname": "", +## "transmittance": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "dissolve": 1.0, +## "ambient": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "diffuse": [ +## 1.0, +## 0.0, +## 0.0 +## ], +## "diffuse_texname": "", +## "ambient_texname": "" +## }, +## "white": { +## "emission": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "specular": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "illum": 0, +## "ior": 1.0, +## "shininess": 1.0, +## "normal_texname": "", +## "specular_texname": "", +## "transmittance": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "dissolve": 1.0, +## "ambient": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "diffuse": [ +## 1.0, +## 1.0, +## 1.0 +## ], +## "diffuse_texname": "", +## "ambient_texname": "" +## }, +## "light": { +## "emission": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "specular": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "illum": 0, +## "ior": 1.0, +## "shininess": 1.0, +## "normal_texname": "", +## "specular_texname": "", +## "transmittance": [ +## 0.0, +## 0.0, +## 0.0 +## ], +## "dissolve": 1.0, +## "ambient": [ +## 20.0, +## 20.0, +## 20.0 +## ], +## "diffuse": [ +## 1.0, +## 1.0, +## 1.0 +## ], +## "diffuse_texname": "", +## "ambient_texname": "" +## } +## } +##} From 93d72326144fdea4ec876ae0494a17106647f065 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 16 Nov 2014 23:39:20 +0100 Subject: [PATCH 006/585] fix shapes / adding setup file --- python/cornell_box_output.json | 601 +++++++++++++++++++++++++++++++++ python/howto.py | 558 +----------------------------- python/main.cpp | 67 ++-- 3 files changed, 637 insertions(+), 589 deletions(-) create mode 100644 python/cornell_box_output.json diff --git a/python/cornell_box_output.json b/python/cornell_box_output.json new file mode 100644 index 00000000..ba2e7e1c --- /dev/null +++ b/python/cornell_box_output.json @@ -0,0 +1,601 @@ +{ + "shapes": { + "ceiling": { + "texcoords": [], + "positions": [ + 556.0, + 548.7999877929688, + 0.0, + 556.0, + 548.7999877929688, + 559.2000122070312, + 0.0, + 548.7999877929688, + 559.2000122070312, + 0.0, + 548.7999877929688, + 0.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "material_ids": [ + 0.0, + 0.0 + ], + "normals": [] + }, + "floor": { + "texcoords": [], + "positions": [ + 552.7999877929688, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 559.2000122070312, + 549.5999755859375, + 0.0, + 559.2000122070312, + 290.0, + 0.0, + 114.0, + 240.0, + 0.0, + 272.0, + 82.0, + 0.0, + 225.0, + 130.0, + 0.0, + 65.0, + 472.0, + 0.0, + 406.0, + 314.0, + 0.0, + 456.0, + 265.0, + 0.0, + 296.0, + 423.0, + 0.0, + 247.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 8.0, + 10.0, + 11.0 + ], + "material_ids": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "normals": [] + }, + "light": { + "texcoords": [], + "positions": [ + 343.0, + 548.0, + 227.0, + 343.0, + 548.0, + 332.0, + 213.0, + 548.0, + 332.0, + 213.0, + 548.0, + 227.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "material_ids": [ + 4.0, + 4.0 + ], + "normals": [] + }, + "green_wall": { + "texcoords": [], + "positions": [ + 0.0, + 0.0, + 559.2000122070312, + 0.0, + 0.0, + 0.0, + 0.0, + 548.7999877929688, + 0.0, + 0.0, + 548.7999877929688, + 559.2000122070312 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "material_ids": [ + 2.0, + 2.0 + ], + "normals": [] + }, + "back_wall": { + "texcoords": [], + "positions": [ + 549.5999755859375, + 0.0, + 559.2000122070312, + 0.0, + 0.0, + 559.2000122070312, + 0.0, + 548.7999877929688, + 559.2000122070312, + 556.0, + 548.7999877929688, + 559.2000122070312 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "material_ids": [ + 0.0, + 0.0 + ], + "normals": [] + }, + "short_block": { + "texcoords": [], + "positions": [ + 130.0, + 165.0, + 65.0, + 82.0, + 165.0, + 225.0, + 240.0, + 165.0, + 272.0, + 290.0, + 165.0, + 114.0, + 290.0, + 0.0, + 114.0, + 290.0, + 165.0, + 114.0, + 240.0, + 165.0, + 272.0, + 240.0, + 0.0, + 272.0, + 130.0, + 0.0, + 65.0, + 130.0, + 165.0, + 65.0, + 290.0, + 165.0, + 114.0, + 290.0, + 0.0, + 114.0, + 82.0, + 0.0, + 225.0, + 82.0, + 165.0, + 225.0, + 130.0, + 165.0, + 65.0, + 130.0, + 0.0, + 65.0, + 240.0, + 0.0, + 272.0, + 240.0, + 165.0, + 272.0, + 82.0, + 165.0, + 225.0, + 82.0, + 0.0, + 225.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 8.0, + 10.0, + 11.0, + 12.0, + 13.0, + 14.0, + 12.0, + 14.0, + 15.0, + 16.0, + 17.0, + 18.0, + 16.0, + 18.0, + 19.0 + ], + "material_ids": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "normals": [] + }, + "tall_block": { + "texcoords": [], + "positions": [ + 423.0, + 0.0, + 247.0, + 423.0, + 330.0, + 247.0, + 472.0, + 330.0, + 406.0, + 472.0, + 0.0, + 406.0, + 472.0, + 0.0, + 406.0, + 472.0, + 330.0, + 406.0, + 314.0, + 330.0, + 456.0, + 314.0, + 0.0, + 456.0, + 314.0, + 0.0, + 456.0, + 314.0, + 330.0, + 456.0, + 265.0, + 330.0, + 296.0, + 265.0, + 0.0, + 296.0, + 265.0, + 0.0, + 296.0, + 265.0, + 330.0, + 296.0, + 423.0, + 330.0, + 247.0, + 423.0, + 0.0, + 247.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 8.0, + 10.0, + 11.0, + 12.0, + 13.0, + 14.0, + 12.0, + 14.0, + 15.0 + ], + "material_ids": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "normals": [] + }, + "red_wall": { + "texcoords": [], + "positions": [ + 552.7999877929688, + 0.0, + 0.0, + 549.5999755859375, + 0.0, + 559.2000122070312, + 556.0, + 548.7999877929688, + 559.2000122070312, + 556.0, + 548.7999877929688, + 0.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "material_ids": [ + 1.0, + 1.0 + ], + "normals": [] + } + }, + "materials": { + "blue": { + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "illum": 0, + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "diffuse_texname": "", + "ambient_texname": "", + "normal_texname": "", + "shininess": 1.0, + "ior": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "", + "diffuse": [ + 0.0, + 0.0, + 1.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "dissolve": 1.0 + }, + "light": { + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "illum": 0, + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "diffuse_texname": "", + "ambient_texname": "", + "normal_texname": "", + "shininess": 1.0, + "ior": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "", + "diffuse": [ + 1.0, + 1.0, + 1.0 + ], + "ambient": [ + 20.0, + 20.0, + 20.0 + ], + "dissolve": 1.0 + }, + "white": { + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "illum": 0, + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "diffuse_texname": "", + "ambient_texname": "", + "normal_texname": "", + "shininess": 1.0, + "ior": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "", + "diffuse": [ + 1.0, + 1.0, + 1.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "dissolve": 1.0 + }, + "green": { + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "illum": 0, + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "diffuse_texname": "", + "ambient_texname": "", + "normal_texname": "", + "shininess": 1.0, + "ior": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "", + "diffuse": [ + 0.0, + 1.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "dissolve": 1.0 + }, + "red": { + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "illum": 0, + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "diffuse_texname": "", + "ambient_texname": "", + "normal_texname": "", + "shininess": 1.0, + "ior": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "", + "diffuse": [ + 1.0, + 0.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "dissolve": 1.0 + } + } +} diff --git a/python/howto.py b/python/howto.py index 099a1af7..c5268215 100644 --- a/python/howto.py +++ b/python/howto.py @@ -1,563 +1,11 @@ import tinyobjloader as tol import json -model = tol.LoadObj("cube.obj") +model = tol.LoadObj("cornell_box.obj") #print(model["shapes"], model["materials"]) print( json.dumps(model, indent=4) ) -#EXAMPLE OUTPUT +#see cornell_box_output.json + -##{ -## "shapes": { -## "left": [ -## [ -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0 -## ], -## [ -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0 -## ], -## [ -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0 -## ], -## [ -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0 -## ], -## [ -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0, -## 2.0 -## ] -## ], -## "bottom": [ -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ] -## ], -## "right": [ -## [ -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0 -## ], -## [ -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0 -## ], -## [ -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0 -## ], -## [ -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0 -## ], -## [ -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0, -## 1.0 -## ] -## ], -## "front": [ -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ] -## ], -## "top": [ -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ] -## ], -## "back": [ -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ], -## [ -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0, -## 0.0 -## ] -## ] -## }, -## "materials": { -## "green": { -## "emission": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "specular": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "illum": 0, -## "ior": 1.0, -## "shininess": 1.0, -## "normal_texname": "", -## "specular_texname": "", -## "transmittance": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "dissolve": 1.0, -## "ambient": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "diffuse": [ -## 0.0, -## 1.0, -## 0.0 -## ], -## "diffuse_texname": "", -## "ambient_texname": "" -## }, -## "blue": { -## "emission": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "specular": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "illum": 0, -## "ior": 1.0, -## "shininess": 1.0, -## "normal_texname": "", -## "specular_texname": "", -## "transmittance": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "dissolve": 1.0, -## "ambient": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "diffuse": [ -## 0.0, -## 0.0, -## 1.0 -## ], -## "diffuse_texname": "", -## "ambient_texname": "" -## }, -## "red": { -## "emission": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "specular": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "illum": 0, -## "ior": 1.0, -## "shininess": 1.0, -## "normal_texname": "", -## "specular_texname": "", -## "transmittance": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "dissolve": 1.0, -## "ambient": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "diffuse": [ -## 1.0, -## 0.0, -## 0.0 -## ], -## "diffuse_texname": "", -## "ambient_texname": "" -## }, -## "white": { -## "emission": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "specular": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "illum": 0, -## "ior": 1.0, -## "shininess": 1.0, -## "normal_texname": "", -## "specular_texname": "", -## "transmittance": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "dissolve": 1.0, -## "ambient": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "diffuse": [ -## 1.0, -## 1.0, -## 1.0 -## ], -## "diffuse_texname": "", -## "ambient_texname": "" -## }, -## "light": { -## "emission": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "specular": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "illum": 0, -## "ior": 1.0, -## "shininess": 1.0, -## "normal_texname": "", -## "specular_texname": "", -## "transmittance": [ -## 0.0, -## 0.0, -## 0.0 -## ], -## "dissolve": 1.0, -## "ambient": [ -## 20.0, -## 20.0, -## 20.0 -## ], -## "diffuse": [ -## 1.0, -## 1.0, -## 1.0 -## ], -## "diffuse_texname": "", -## "ambient_texname": "" -## } -## } -##} diff --git a/python/main.cpp b/python/main.cpp index 636a24d5..8999563d 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -5,6 +5,7 @@ // model = tol.LoadObj(name) // print(model["shapes"]) // print(model["materials"] + #include #include #include "../tiny_obj_loader.h" @@ -30,7 +31,7 @@ extern "C" static PyObject* pyLoadObj(PyObject* self, PyObject* args) { - PyObject *rtntpl, *pyshapes, *pymaterials; + PyObject *rtndict, *pyshapes, *pymaterials; char const* filename; std::vector shapes; std::vector materials; @@ -42,48 +43,46 @@ pyLoadObj(PyObject* self, PyObject* args) pyshapes = PyDict_New(); pymaterials = PyDict_New(); - rtntpl = PyDict_New(); + rtndict = PyDict_New(); for (std::vector::iterator shape = shapes.begin() ; shape != shapes.end(); shape++) { - PyObject *meshobj; - PyObject *positions, *normals, *texcoords, *indices, *material_ids; - - meshobj = PyTuple_New(5); - positions = PyList_New(0); - normals = PyList_New(0); - texcoords = PyList_New(0); - indices = PyList_New(0); - material_ids = PyList_New(0); + PyObject *meshobj, *current; + char *current_name; + vectd vect; + meshobj = PyDict_New(); tinyobj::mesh_t cm = (*shape).mesh; - for (int i = 0; i <= 4 ; i++ ) - { - PyObject *current; - vectd vect; - switch (i) - { - case 0: current = positions; - vect = vectd(cm.positions.begin(), cm.positions.end()); - case 1: current = normals; - vect = vectd(cm.normals.begin(), cm.normals.end()); - case 2: current = texcoords; - vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); - case 3: current = indices; - vect = vectd(cm.indices.begin(), cm.indices.end()); - case 4: current = material_ids; - vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); - } - - for (std::vector::iterator it = vect.begin() ; + for (int i = 0; i <= 4; i++ ) + { + current = PyList_New(0); + + if (i == 0){ + current_name = "positions"; + vect = vectd(cm.positions.begin(), cm.positions.end()); } + else if (i==1){ + current_name = "normals"; + vect = vectd(cm.normals.begin(), cm.normals.end()); } + else if (i == 2) { + current_name = "texcoords"; + vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); } + else if (i==3) { + current_name = "indicies"; + vect = vectd(cm.indices.begin(), cm.indices.end()); } + else if (i == 4) { + current_name = "material_ids"; + vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); } + + for (vectd::iterator it = vect.begin() ; it != vect.end(); it++) { PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it)); } - PyTuple_SetItem(meshobj, i, current); + PyDict_SetItemString(meshobj, current_name, current); + } PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj); @@ -111,10 +110,10 @@ pyLoadObj(PyObject* self, PyObject* args) PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj); } - PyDict_SetItemString(rtntpl, "shapes", pyshapes); - PyDict_SetItemString(rtntpl, "materials", pymaterials); + PyDict_SetItemString(rtndict, "shapes", pyshapes); + PyDict_SetItemString(rtndict, "materials", pymaterials); - return rtntpl; + return rtndict; } From 9587ad9aee5b5801ab1b3eb244bbc06ed38b891a Mon Sep 17 00:00:00 2001 From: root Date: Sun, 16 Nov 2014 23:53:21 +0100 Subject: [PATCH 007/585] cleaning code.. --- python/main.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/python/main.cpp b/python/main.cpp index 8999563d..81f74767 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -31,8 +31,13 @@ extern "C" static PyObject* pyLoadObj(PyObject* self, PyObject* args) { - PyObject *rtndict, *pyshapes, *pymaterials; + PyObject *rtndict, *pyshapes, *pymaterials, + *current, *meshobj; + char const* filename; + char *current_name; + vectd vect; + std::vector shapes; std::vector materials; @@ -48,10 +53,6 @@ pyLoadObj(PyObject* self, PyObject* args) for (std::vector::iterator shape = shapes.begin() ; shape != shapes.end(); shape++) { - PyObject *meshobj, *current; - char *current_name; - vectd vect; - meshobj = PyDict_New(); tinyobj::mesh_t cm = (*shape).mesh; @@ -62,13 +63,13 @@ pyLoadObj(PyObject* self, PyObject* args) if (i == 0){ current_name = "positions"; vect = vectd(cm.positions.begin(), cm.positions.end()); } - else if (i==1){ + else if (i == 1){ current_name = "normals"; vect = vectd(cm.normals.begin(), cm.normals.end()); } else if (i == 2) { current_name = "texcoords"; vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); } - else if (i==3) { + else if (i == 3) { current_name = "indicies"; vect = vectd(cm.indices.begin(), cm.indices.end()); } else if (i == 4) { From aabdad4bc48067ce0f3a6535257b06bd22c0238a Mon Sep 17 00:00:00 2001 From: root Date: Mon, 17 Nov 2014 22:06:25 +0100 Subject: [PATCH 008/585] changed to switch structure --- python/main.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/python/main.cpp b/python/main.cpp index 81f74767..b2c6e96f 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -60,23 +60,27 @@ pyLoadObj(PyObject* self, PyObject* args) { current = PyList_New(0); - if (i == 0){ + switch(i) { + + case 0: current_name = "positions"; - vect = vectd(cm.positions.begin(), cm.positions.end()); } - else if (i == 1){ + vect = vectd(cm.positions.begin(), cm.positions.end()); break; + case 1: current_name = "normals"; - vect = vectd(cm.normals.begin(), cm.normals.end()); } - else if (i == 2) { + vect = vectd(cm.normals.begin(), cm.normals.end()); break; + case 2: current_name = "texcoords"; - vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); } - else if (i == 3) { + vect = vectd(cm.texcoords.begin(), cm.texcoords.end()); break; + case 3: current_name = "indicies"; - vect = vectd(cm.indices.begin(), cm.indices.end()); } - else if (i == 4) { + vect = vectd(cm.indices.begin(), cm.indices.end()); break; + case 4: current_name = "material_ids"; - vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); } - - for (vectd::iterator it = vect.begin() ; + vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); break; + + } + + for (vectd::iterator it = vect.begin() ; it != vect.end(); it++) { PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it)); From c5ed61f358b0c088e5cc1c68e38197383adb2ef9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 30 Nov 2014 11:59:55 +0900 Subject: [PATCH 009/585] Add link to IBLBaker. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 033bbe63..38d65876 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ TinyObjLoader is successfully used in ... * pbrt-v2 https://https://github.com/mmp/pbrt-v2 * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 * mallie https://lighttransport.github.io/mallie +* IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ * Your project here! Features @@ -58,7 +59,7 @@ Polygon is converted into triangle. TODO ---- -* Support quad polygon and some tags for OpenSubdiv http://graphics.pixar.com/opensubdiv/ +- [ ] Support quad polygon and some tags for OpenSubdiv http://graphics.pixar.com/opensubdiv/ License ------- From b5352a642b2d347b4afd0b9d5043ee3a2cd023e5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 17 Jan 2015 22:27:20 +0900 Subject: [PATCH 010/585] Add drone.yml --- .drone.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .drone.yml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..33e98b29 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,7 @@ +image: bradrydzewski/base +script: + - make +notify: + email: + recipients: + - syoyo@lighttransport.com From 4779593e440104dd9d35d50c413bf83bb2038900 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 17 Jan 2015 22:30:02 +0900 Subject: [PATCH 011/585] Update drone.yml. --- .drone.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.drone.yml b/.drone.yml index 33e98b29..1724277a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,5 +1,8 @@ image: bradrydzewski/base script: + - curl -L -O premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true + - chmod +x ./premake4 + - ./premake4 gmake - make notify: email: From 276c7e151e53b9171ad98e257b71965c6f28a3f1 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 17 Jan 2015 22:37:31 +0900 Subject: [PATCH 012/585] Update drone.yml Add Zup conversion in obj_writer. --- .drone.yml | 2 +- examples/obj_sticher/obj_sticher.cc | 4 ++- examples/obj_sticher/obj_writer.cc | 40 +++++++++++++++++++++-------- examples/obj_sticher/obj_writer.h | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/.drone.yml b/.drone.yml index 1724277a..7fe6321e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,6 +1,6 @@ image: bradrydzewski/base script: - - curl -L -O premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true + - curl -L -o premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true - chmod +x ./premake4 - ./premake4 gmake - make diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index 18332168..8ec7a28f 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -81,6 +81,7 @@ main( std::vector shapes; std::vector materials; shapes.resize(num_objfiles); + materials.resize(num_objfiles); for (int i = 0; i < num_objfiles; i++) { std::cout << "Loading " << argv[i+1] << " ... " << std::flush; @@ -98,7 +99,8 @@ main( std::vector out_material; StichObjs(out_shape, out_material, shapes, materials); - bool ret = WriteObj(out_filename, out_shape, out_material); + bool coordTransform = true; + bool ret = WriteObj(out_filename, out_shape, out_material, coordTransform); assert(ret); return 0; diff --git a/examples/obj_sticher/obj_writer.cc b/examples/obj_sticher/obj_writer.cc index bb12457f..2c8bd7b0 100644 --- a/examples/obj_sticher/obj_writer.cc +++ b/examples/obj_sticher/obj_writer.cc @@ -38,7 +38,7 @@ bool WriteMat(const std::string& filename, const std::vector& shapes, const std::vector& materials) { +bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials, bool coordTransform) { FILE* fp = fopen(filename.c_str(), "w"); if (!fp) { fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); @@ -74,10 +74,17 @@ bool WriteObj(const std::string& filename, const std::vector& for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { for (int j = 0; j < 3; j++) { int idx = shapes[i].mesh.indices[3*k+j]; - fprintf(fp, "v %f %f %f\n", - shapes[i].mesh.positions[3*idx+0], - shapes[i].mesh.positions[3*idx+1], - shapes[i].mesh.positions[3*idx+2]); + if (coordTransform) { + fprintf(fp, "v %f %f %f\n", + shapes[i].mesh.positions[3*idx+0], + shapes[i].mesh.positions[3*idx+2], + -shapes[i].mesh.positions[3*idx+1]); + } else { + fprintf(fp, "v %f %f %f\n", + shapes[i].mesh.positions[3*idx+0], + shapes[i].mesh.positions[3*idx+1], + shapes[i].mesh.positions[3*idx+2]); + } } } @@ -86,10 +93,17 @@ bool WriteObj(const std::string& filename, const std::vector& for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { for (int j = 0; j < 3; j++) { int idx = shapes[i].mesh.indices[3*k+j]; - fprintf(fp, "vn %f %f %f\n", - shapes[i].mesh.normals[3*idx+0], - shapes[i].mesh.normals[3*idx+1], - shapes[i].mesh.normals[3*idx+2]); + if (coordTransform) { + fprintf(fp, "vn %f %f %f\n", + shapes[i].mesh.normals[3*idx+0], + shapes[i].mesh.normals[3*idx+2], + -shapes[i].mesh.normals[3*idx+1]); + } else { + fprintf(fp, "vn %f %f %f\n", + shapes[i].mesh.normals[3*idx+0], + shapes[i].mesh.normals[3*idx+1], + shapes[i].mesh.normals[3*idx+2]); + } } } } @@ -119,6 +133,10 @@ bool WriteObj(const std::string& filename, const std::vector& int v1 = (3*k + 1) + 1 + v_offset; int v2 = (3*k + 2) + 1 + v_offset; + int vt0 = (3*k + 0) + 1 + vt_offset; + int vt1 = (3*k + 1) + 1 + vt_offset; + int vt2 = (3*k + 2) + 1 + vt_offset; + int material_id = shapes[i].mesh.material_ids[k]; if (material_id != prev_material_id) { std::string material_name = materials[material_id].name; @@ -128,7 +146,7 @@ bool WriteObj(const std::string& filename, const std::vector& if (has_vn && has_vt) { fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", - v0, v0, v0, v1, v1, v1, v2, v2, v2); + v0, vt0, v0, v1, vt1, v1, v2, vt2, v2); } else if (has_vn && !has_vt) { fprintf(fp, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2); } else if (!has_vn && has_vt) { @@ -141,7 +159,7 @@ bool WriteObj(const std::string& filename, const std::vector& v_offset += shapes[i].mesh.indices.size(); //vn_offset += shapes[i].mesh.normals.size() / 3; - //vt_offset += shapes[i].mesh.texcoords.size() / 2; + vt_offset += shapes[i].mesh.texcoords.size() / 2; } diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h index 00cd792f..bb367b67 100644 --- a/examples/obj_sticher/obj_writer.h +++ b/examples/obj_sticher/obj_writer.h @@ -3,7 +3,7 @@ #include "../../tiny_obj_loader.h" -extern bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials); +extern bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials, bool coordTransform = false); #endif // __OBJ_WRITER_H__ From fae5b03e7c7cfa7392609e39893348a27f9a0335 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 17 Jan 2015 23:04:15 +0900 Subject: [PATCH 013/585] Update README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 38d65876..08bf9341 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ TinyObjLoader is successfully used in ... * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 * mallie https://lighttransport.github.io/mallie * IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ +* Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf * Your project here! Features From 41db59cde5e80147f13828eb0187ae00b590a093 Mon Sep 17 00:00:00 2001 From: Maurice Laveaux Date: Mon, 26 Jan 2015 17:32:56 +0100 Subject: [PATCH 014/585] Fixed cmake warning that targets shouldn't be named test. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94ec3302..ad1b8dfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,8 +26,8 @@ add_library(tinyobjloader ${tinyobjloader-Source} ) -add_executable(test ${tinyobjloader-Test-Source}) -target_link_libraries(test tinyobjloader) +add_executable(test_loader ${tinyobjloader-Test-Source}) +target_link_libraries(test_loader tinyobjloader) add_executable(obj_sticher ${tinyobjloader-examples-objsticher}) target_link_libraries(obj_sticher tinyobjloader) From 0a500b77e78335ce324cdc213e602f2ce55109f5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 2 Feb 2015 17:43:08 +0900 Subject: [PATCH 015/585] Small update for README. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 08bf9341..93d98808 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ http://syoyo.github.io/tinyobjloader/ Tiny but poweful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. -Good for embedding .obj loader to your (global illumination) renderer ;-) +`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) + What's new ---------- @@ -50,7 +51,7 @@ Features * Texcoord * Normal * Material - * Unknown material attributes are treated as key-value. + * Unknown material attributes are treated as key-value(value is string). Notes ----- From 672f252195722635843962b4c1a191f864fe3852 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 7 Feb 2015 00:01:37 +0900 Subject: [PATCH 016/585] Fix per-face material. --- cornell_box_multimaterial.obj | 146 ++++++++++++++++++++++++++++++++++ tiny_obj_loader.cc | 9 ++- 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 cornell_box_multimaterial.obj diff --git a/cornell_box_multimaterial.obj b/cornell_box_multimaterial.obj new file mode 100644 index 00000000..68093bea --- /dev/null +++ b/cornell_box_multimaterial.obj @@ -0,0 +1,146 @@ +# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. +# original cornell box data + # comment + +# empty line including some space + + +mtllib cornell_box.mtl + +o floor +usemtl white +v 552.8 0.0 0.0 +v 0.0 0.0 0.0 +v 0.0 0.0 559.2 +v 549.6 0.0 559.2 + +v 130.0 0.0 65.0 +v 82.0 0.0 225.0 +v 240.0 0.0 272.0 +v 290.0 0.0 114.0 + +v 423.0 0.0 247.0 +v 265.0 0.0 296.0 +v 314.0 0.0 456.0 +v 472.0 0.0 406.0 + +f 1 2 3 4 +f 8 7 6 5 +f 12 11 10 9 + +o light +usemtl light +v 343.0 548.0 227.0 +v 343.0 548.0 332.0 +v 213.0 548.0 332.0 +v 213.0 548.0 227.0 +f -4 -3 -2 -1 + +o ceiling +usemtl white +v 556.0 548.8 0.0 +v 556.0 548.8 559.2 +v 0.0 548.8 559.2 +v 0.0 548.8 0.0 +f -4 -3 -2 -1 + +o back_wall +usemtl white +v 549.6 0.0 559.2 +v 0.0 0.0 559.2 +v 0.0 548.8 559.2 +v 556.0 548.8 559.2 +f -4 -3 -2 -1 + +o front_wall +usemtl blue +v 549.6 0.0 0 +v 0.0 0.0 0 +v 0.0 548.8 0 +v 556.0 548.8 0 +#f -1 -2 -3 -4 + +o green_wall +usemtl green +v 0.0 0.0 559.2 +v 0.0 0.0 0.0 +v 0.0 548.8 0.0 +v 0.0 548.8 559.2 +f -4 -3 -2 -1 + +o red_wall +usemtl red +v 552.8 0.0 0.0 +v 549.6 0.0 559.2 +v 556.0 548.8 559.2 +v 556.0 548.8 0.0 +f -4 -3 -2 -1 + +o short_block +usemtl white + +v 130.0 165.0 65.0 +v 82.0 165.0 225.0 +v 240.0 165.0 272.0 +v 290.0 165.0 114.0 +f -4 -3 -2 -1 + +v 290.0 0.0 114.0 +v 290.0 165.0 114.0 +v 240.0 165.0 272.0 +v 240.0 0.0 272.0 +f -4 -3 -2 -1 + +v 130.0 0.0 65.0 +v 130.0 165.0 65.0 +v 290.0 165.0 114.0 +v 290.0 0.0 114.0 +f -4 -3 -2 -1 + +v 82.0 0.0 225.0 +v 82.0 165.0 225.0 +v 130.0 165.0 65.0 +v 130.0 0.0 65.0 +f -4 -3 -2 -1 + +v 240.0 0.0 272.0 +v 240.0 165.0 272.0 +v 82.0 165.0 225.0 +v 82.0 0.0 225.0 +f -4 -3 -2 -1 + +o tall_block +usemtl white + +v 423.0 330.0 247.0 +v 265.0 330.0 296.0 +v 314.0 330.0 456.0 +v 472.0 330.0 406.0 +f -4 -3 -2 -1 + +usemtl white +v 423.0 0.0 247.0 +v 423.0 330.0 247.0 +v 472.0 330.0 406.0 +v 472.0 0.0 406.0 +f -4 -3 -2 -1 + +v 472.0 0.0 406.0 +v 472.0 330.0 406.0 +v 314.0 330.0 456.0 +v 314.0 0.0 456.0 +f -4 -3 -2 -1 +usemtl green + +v 314.0 0.0 456.0 +v 314.0 330.0 456.0 +v 265.0 330.0 296.0 +v 265.0 0.0 296.0 +f -4 -3 -2 -1 + +v 265.0 0.0 296.0 +v 265.0 330.0 296.0 +v 423.0 330.0 247.0 +v 423.0 0.0 247.0 +f -4 -3 -2 -1 + diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index b73e7662..2b7bd9c4 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -1,10 +1,11 @@ // -// Copyright 2012-2013, Syoyo Fujita. +// Copyright 2012-2015, Syoyo Fujita. // // Licensed under 2-clause BSD liecense. // // +// version 0.9.8: Fix multi-materials(per-face material ID). // version 0.9.7: Support multi-materials(per-face material ID) per object/group. // version 0.9.6: Support Ni(index of refraction) mtl parameter. // Parse transmittance material parameter correctly. @@ -620,7 +621,11 @@ std::string LoadObj( token += 7; sscanf(token, "%s", namebuf); - faceGroup.clear(); + // Create face group per material. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); + if (ret) { + faceGroup.clear(); + } if (material_map.find(namebuf) != material_map.end()) { material = material_map[namebuf]; From a67a60d19fd423449c9e2e6ba6ca2071c1f26d72 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 7 Feb 2015 00:04:17 +0900 Subject: [PATCH 017/585] Update README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 93d98808..7c3bb0ae 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- +* Feb 06, 2015 : Fix parsing multi-material object * Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo! * Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran! * Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse! From f28d2eef887f392c9ce46e18594e8021cdfac25b Mon Sep 17 00:00:00 2001 From: CoolerExtreme Date: Thu, 12 Feb 2015 23:31:06 +0530 Subject: [PATCH 018/585] Fix parseString ? and slight change to fixIndex function parseString seemed to not increment token after it used strspn to get the length of the whitespace characters at the beginning of token. So strcspn called right after that would return 0 and the created string would be an empty string. Seems to have been working so far since it gets passed strings that don't begin with whitespace characters. --- tiny_obj_loader.cc | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 2b7bd9c4..dde2bc75 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -67,26 +67,18 @@ static inline bool isNewLine(const char c) { // Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { - int i; - - if (idx > 0) { - i = idx - 1; - } else if (idx == 0) { - i = 0; - } else { // negative value = relative - i = n + idx; - } - return i; + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative } static inline std::string parseString(const char*& token) { std::string s; - int b = strspn(token, " \t"); + token += strspn(token, " \t"); int e = strcspn(token, " \t\r"); - s = std::string(&token[b], &token[e]); - - token += (e - b); + s = std::string(&token, &token[e]); + token += e; return s; } From 011e1b3ebdc3e16f3f0a8d8ee8f537a067f06e91 Mon Sep 17 00:00:00 2001 From: CoolerExtreme Date: Fri, 13 Feb 2015 06:37:37 +0530 Subject: [PATCH 019/585] Slight typo --- tiny_obj_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index dde2bc75..a974d6ea 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -77,7 +77,7 @@ static inline std::string parseString(const char*& token) std::string s; token += strspn(token, " \t"); int e = strcspn(token, " \t\r"); - s = std::string(&token, &token[e]); + s = std::string(token, &token[e]); token += e; return s; } From 99792758354e2f97231def04a38f020f56d841e9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 15 Feb 2015 16:51:38 +0900 Subject: [PATCH 020/585] Format source code. --- tiny_obj_loader.cc | 343 +++++++++++++++++++++------------------------ tiny_obj_loader.h | 115 +++++++-------- 2 files changed, 213 insertions(+), 245 deletions(-) mode change 100755 => 100644 tiny_obj_loader.cc diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc old mode 100755 new mode 100644 index 2b7bd9c4..05c5aad1 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -1,12 +1,13 @@ // // Copyright 2012-2015, Syoyo Fujita. -// +// // Licensed under 2-clause BSD liecense. // // // version 0.9.8: Fix multi-materials(per-face material ID). -// version 0.9.7: Support multi-materials(per-face material ID) per object/group. +// version 0.9.7: Support multi-materials(per-face material ID) per +// object/group. // version 0.9.6: Support Ni(index of refraction) mtl parameter. // Parse transmittance material parameter correctly. // version 0.9.5: Parse multiple group name. @@ -18,7 +19,6 @@ // version 0.9.0: Initial // - #include #include #include @@ -35,17 +35,19 @@ namespace tinyobj { struct vertex_index { int v_idx, vt_idx, vn_idx; - vertex_index() {}; - vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}; - vertex_index(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}; - + vertex_index(){}; + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){}; + vertex_index(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){}; }; // for std::map -static inline bool operator<(const vertex_index& a, const vertex_index& b) -{ - if (a.v_idx != b.v_idx) return (a.v_idx < b.v_idx); - if (a.vn_idx != b.vn_idx) return (a.vn_idx < b.vn_idx); - if (a.vt_idx != b.vt_idx) return (a.vt_idx < b.vt_idx); +static inline bool operator<(const vertex_index &a, const vertex_index &b) { + if (a.v_idx != b.v_idx) + return (a.v_idx < b.v_idx); + if (a.vn_idx != b.vn_idx) + return (a.vn_idx < b.vn_idx); + if (a.vt_idx != b.vt_idx) + return (a.vt_idx < b.vt_idx); return false; } @@ -56,17 +58,14 @@ struct obj_shape { std::vector vt; }; -static inline bool isSpace(const char c) { - return (c == ' ') || (c == '\t'); -} +static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } static inline bool isNewLine(const char c) { return (c == '\r') || (c == '\n') || (c == '\0'); } -// Make index zero-base, and also support relative index. -static inline int fixIndex(int idx, int n) -{ +// Make index zero-base, and also support relative index. +static inline int fixIndex(int idx, int n) { int i; if (idx > 0) { @@ -79,8 +78,7 @@ static inline int fixIndex(int idx, int n) return i; } -static inline std::string parseString(const char*& token) -{ +static inline std::string parseString(const char *&token) { std::string s; int b = strspn(token, " \t"); int e = strcspn(token, " \t\r"); @@ -90,89 +88,73 @@ static inline std::string parseString(const char*& token) return s; } -static inline int parseInt(const char*& token) -{ +static inline int parseInt(const char *&token) { token += strspn(token, " \t"); int i = atoi(token); token += strcspn(token, " \t\r"); return i; } -static inline float parseFloat(const char*& token) -{ +static inline float parseFloat(const char *&token) { token += strspn(token, " \t"); float f = (float)atof(token); token += strcspn(token, " \t\r"); return f; } -static inline void parseFloat2( - float& x, float& y, - const char*& token) -{ +static inline void parseFloat2(float &x, float &y, const char *&token) { x = parseFloat(token); y = parseFloat(token); } -static inline void parseFloat3( - float& x, float& y, float& z, - const char*& token) -{ +static inline void parseFloat3(float &x, float &y, float &z, + const char *&token) { x = parseFloat(token); y = parseFloat(token); z = parseFloat(token); } - // Parse triples: i, i/j/k, i//k, i/j -static vertex_index parseTriple( - const char* &token, - int vsize, - int vnsize, - int vtsize) -{ - vertex_index vi(-1); - - vi.v_idx = fixIndex(atoi(token), vsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return vi; - } - token++; - - // i//k - if (token[0] == '/') { - token++; - vi.vn_idx = fixIndex(atoi(token), vnsize); - token += strcspn(token, "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = fixIndex(atoi(token), vtsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return vi; - } +static vertex_index parseTriple(const char *&token, int vsize, int vnsize, + int vtsize) { + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi(token), vsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + token++; - // i/j/k - token++; // skip '/' + // i//k + if (token[0] == '/') { + token++; vi.vn_idx = fixIndex(atoi(token), vnsize); token += strcspn(token, "/ \t\r"); - return vi; + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi(token), vtsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + + // i/j/k + token++; // skip '/' + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; } static unsigned int -updateVertex( - std::map& vertexCache, - std::vector& positions, - std::vector& normals, - std::vector& texcoords, - const std::vector& in_positions, - const std::vector& in_normals, - const std::vector& in_texcoords, - const vertex_index& i) -{ +updateVertex(std::map &vertexCache, + std::vector &positions, std::vector &normals, + std::vector &texcoords, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, const vertex_index &i) { const std::map::iterator it = vertexCache.find(i); if (it != vertexCache.end()) { @@ -180,21 +162,21 @@ updateVertex( return it->second; } - assert(in_positions.size() > (unsigned int) (3*i.v_idx+2)); + assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2)); - positions.push_back(in_positions[3*i.v_idx+0]); - positions.push_back(in_positions[3*i.v_idx+1]); - positions.push_back(in_positions[3*i.v_idx+2]); + positions.push_back(in_positions[3 * i.v_idx + 0]); + positions.push_back(in_positions[3 * i.v_idx + 1]); + positions.push_back(in_positions[3 * i.v_idx + 2]); if (i.vn_idx >= 0) { - normals.push_back(in_normals[3*i.vn_idx+0]); - normals.push_back(in_normals[3*i.vn_idx+1]); - normals.push_back(in_normals[3*i.vn_idx+2]); + normals.push_back(in_normals[3 * i.vn_idx + 0]); + normals.push_back(in_normals[3 * i.vn_idx + 1]); + normals.push_back(in_normals[3 * i.vn_idx + 2]); } if (i.vt_idx >= 0) { - texcoords.push_back(in_texcoords[2*i.vt_idx+0]); - texcoords.push_back(in_texcoords[2*i.vt_idx+1]); + texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]); + texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]); } unsigned int idx = positions.size() / 3 - 1; @@ -203,13 +185,13 @@ updateVertex( return idx; } -void InitMaterial(material_t& material) { +void InitMaterial(material_t &material) { material.name = ""; material.ambient_texname = ""; material.diffuse_texname = ""; material.specular_texname = ""; material.normal_texname = ""; - for (int i = 0; i < 3; i ++) { + for (int i = 0; i < 3; i++) { material.ambient[i] = 0.f; material.diffuse[i] = 0.f; material.specular[i] = 0.f; @@ -223,25 +205,20 @@ void InitMaterial(material_t& material) { material.unknown_parameter.clear(); } -static bool -exportFaceGroupToShape( - shape_t& shape, - std::map vertexCache, - const std::vector &in_positions, - const std::vector &in_normals, - const std::vector &in_texcoords, - const std::vector >& faceGroup, - const int material_id, - const std::string &name, - bool clearCache) -{ +static bool exportFaceGroupToShape( + shape_t &shape, std::map vertexCache, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, + const std::vector> &faceGroup, + const int material_id, const std::string &name, bool clearCache) { if (faceGroup.empty()) { return false; } // Flatten vertices and indices for (size_t i = 0; i < faceGroup.size(); i++) { - const std::vector& face = faceGroup[i]; + const std::vector &face = faceGroup[i]; vertex_index i0 = face[0]; vertex_index i1(-1); @@ -254,9 +231,15 @@ exportFaceGroupToShape( i1 = i2; i2 = face[k]; - unsigned int v0 = updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); - unsigned int v1 = updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); - unsigned int v2 = updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); + unsigned int v0 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); + unsigned int v1 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); + unsigned int v2 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); shape.mesh.indices.push_back(v0); shape.mesh.indices.push_back(v1); @@ -264,30 +247,26 @@ exportFaceGroupToShape( shape.mesh.material_ids.push_back(material_id); } - } shape.name = name; if (clearCache) - vertexCache.clear(); + vertexCache.clear(); return true; - } -std::string LoadMtl ( - std::map& material_map, - std::vector& materials, - std::istream& inStream) -{ +std::string LoadMtl(std::map &material_map, + std::vector &materials, + std::istream &inStream) { material_map.clear(); std::stringstream err; material_t material; - - int maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. while (inStream.peek() != -1) { inStream.getline(&buf[0], maxchars); @@ -295,10 +274,12 @@ std::string LoadMtl ( // Trim newline '\r\n' or '\n' if (linebuf.size() > 0) { - if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); } if (linebuf.size() > 0) { - if (linebuf[linebuf.size()-1] == '\r') linebuf.erase(linebuf.size()-1); + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); } // Skip if empty line. @@ -307,21 +288,23 @@ std::string LoadMtl ( } // Skip leading space. - const char* token = linebuf.c_str(); + const char *token = linebuf.c_str(); token += strspn(token, " \t"); assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line + // new mtl if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { // flush previous material. - if (!material.name.empty()) - { - material_map.insert(std::pair(material.name, materials.size())); - materials.push_back(material); + if (!material.name.empty()) { + material_map.insert( + std::pair(material.name, materials.size())); + materials.push_back(material); } // initial temporary material @@ -334,7 +317,7 @@ std::string LoadMtl ( material.name = namebuf; continue; } - + // ambient if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { token += 2; @@ -345,7 +328,7 @@ std::string LoadMtl ( material.ambient[2] = b; continue; } - + // diffuse if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { token += 2; @@ -356,7 +339,7 @@ std::string LoadMtl ( material.diffuse[2] = b; continue; } - + // specular if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { token += 2; @@ -367,7 +350,7 @@ std::string LoadMtl ( material.specular[2] = b; continue; } - + // transmittance if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { token += 2; @@ -387,7 +370,7 @@ std::string LoadMtl ( } // emission - if(token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { + if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -398,7 +381,7 @@ std::string LoadMtl ( } // shininess - if(token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { + if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { token += 2; material.shininess = parseFloat(token); continue; @@ -452,29 +435,29 @@ std::string LoadMtl ( } // unknown parameter - const char* _space = strchr(token, ' '); - if(!_space) { + const char *_space = strchr(token, ' '); + if (!_space) { _space = strchr(token, '\t'); } - if(_space) { + if (_space) { int len = _space - token; std::string key(token, len); std::string value = _space + 1; - material.unknown_parameter.insert(std::pair(key, value)); + material.unknown_parameter.insert( + std::pair(key, value)); } } // flush last material. - material_map.insert(std::pair(material.name, materials.size())); + material_map.insert( + std::pair(material.name, materials.size())); materials.push_back(material); return err.str(); } -std::string MaterialFileReader::operator() ( - const std::string& matId, - std::vector& materials, - std::map& matMap) -{ +std::string MaterialFileReader::operator()(const std::string &matId, + std::vector &materials, + std::map &matMap) { std::string filepath; if (!m_mtlBasePath.empty()) { @@ -487,13 +470,9 @@ std::string MaterialFileReader::operator() ( return LoadMtl(matMap, materials, matIStream); } -std::string -LoadObj( - std::vector& shapes, - std::vector& materials, // [output] - const char* filename, - const char* mtl_basepath) -{ +std::string LoadObj(std::vector &shapes, + std::vector &materials, // [output] + const char *filename, const char *mtl_basepath) { shapes.clear(); @@ -509,34 +488,31 @@ LoadObj( if (mtl_basepath) { basePath = mtl_basepath; } - MaterialFileReader matFileReader( basePath ); - + MaterialFileReader matFileReader(basePath); + return LoadObj(shapes, materials, ifs, matFileReader); } -std::string LoadObj( - std::vector& shapes, - std::vector& materials, // [output] - std::istream& inStream, - MaterialReader& readMatFn) -{ +std::string LoadObj(std::vector &shapes, + std::vector &materials, // [output] + std::istream &inStream, MaterialReader &readMatFn) { std::stringstream err; std::vector v; std::vector vn; std::vector vt; - std::vector > faceGroup; + std::vector> faceGroup; std::string name; // material std::map material_map; std::map vertexCache; - int material = -1; + int material = -1; shape_t shape; - int maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. while (inStream.peek() != -1) { inStream.getline(&buf[0], maxchars); @@ -544,10 +520,12 @@ std::string LoadObj( // Trim newline '\r\n' or '\n' if (linebuf.size() > 0) { - if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); } if (linebuf.size() > 0) { - if (linebuf[linebuf.size()-1] == '\r') linebuf.erase(linebuf.size()-1); + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); } // Skip if empty line. @@ -556,13 +534,15 @@ std::string LoadObj( } // Skip leading space. - const char* token = linebuf.c_str(); + const char *token = linebuf.c_str(); token += strspn(token, " \t"); assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line // vertex if (token[0] == 'v' && isSpace((token[1]))) { @@ -603,14 +583,15 @@ std::string LoadObj( std::vector face; while (!isNewLine(token[0])) { - vertex_index vi = parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2); + vertex_index vi = + parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2); face.push_back(vi); int n = strspn(token, " \t\r"); token += n; } faceGroup.push_back(face); - + continue; } @@ -622,7 +603,8 @@ std::string LoadObj( sscanf(token, "%s", namebuf); // Create face group per material. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); if (ret) { faceGroup.clear(); } @@ -635,7 +617,6 @@ std::string LoadObj( } continue; - } // load mtl @@ -643,13 +624,13 @@ std::string LoadObj( char namebuf[4096]; token += 7; sscanf(token, "%s", namebuf); - + std::string err_mtl = readMatFn(namebuf, materials, material_map); if (!err_mtl.empty()) { - faceGroup.clear(); // for safety + faceGroup.clear(); // for safety return err_mtl; } - + continue; } @@ -657,14 +638,15 @@ std::string LoadObj( if (token[0] == 'g' && isSpace((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); if (ret) { shapes.push_back(shape); } shape = shape_t(); - //material = -1; + // material = -1; faceGroup.clear(); std::vector names; @@ -690,12 +672,13 @@ std::string LoadObj( if (token[0] == 'o' && isSpace((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); if (ret) { shapes.push_back(shape); } - //material = -1; + // material = -1; faceGroup.clear(); shape = shape_t(); @@ -705,21 +688,19 @@ std::string LoadObj( sscanf(token, "%s", namebuf); name = std::string(namebuf); - continue; } // Ignore unknown command. } - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, + material, name, true); if (ret) { shapes.push_back(shape); } - faceGroup.clear(); // for safety + faceGroup.clear(); // for safety return err.str(); } - - } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index a58d7bed..dbd5f708 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -12,68 +12,61 @@ namespace tinyobj { -typedef struct -{ - std::string name; +typedef struct { + std::string name; - float ambient[3]; - float diffuse[3]; - float specular[3]; - float transmittance[3]; - float emission[3]; - float shininess; - float ior; // index of refraction - float dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; - std::string ambient_texname; - std::string diffuse_texname; - std::string specular_texname; - std::string normal_texname; - std::map unknown_parameter; + std::string ambient_texname; + std::string diffuse_texname; + std::string specular_texname; + std::string normal_texname; + std::map unknown_parameter; } material_t; -typedef struct -{ - std::vector positions; - std::vector normals; - std::vector texcoords; - std::vector indices; - std::vector material_ids; // per-mesh material ID +typedef struct { + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; + std::vector material_ids; // per-mesh material ID } mesh_t; -typedef struct -{ - std::string name; - mesh_t mesh; +typedef struct { + std::string name; + mesh_t mesh; } shape_t; -class MaterialReader -{ +class MaterialReader { public: - MaterialReader(){} - virtual ~MaterialReader(){} + MaterialReader() {} + virtual ~MaterialReader() {} - virtual std::string operator() ( - const std::string& matId, - std::vector& materials, - std::map& matMap) = 0; + virtual std::string operator()(const std::string &matId, + std::vector &materials, + std::map &matMap) = 0; }; -class MaterialFileReader: - public MaterialReader -{ - public: - MaterialFileReader(const std::string& mtl_basepath): m_mtlBasePath(mtl_basepath) {} - virtual ~MaterialFileReader() {} - virtual std::string operator() ( - const std::string& matId, - std::vector& materials, - std::map& matMap); +class MaterialFileReader : public MaterialReader { +public: + MaterialFileReader(const std::string &mtl_basepath) + : m_mtlBasePath(mtl_basepath) {} + virtual ~MaterialFileReader() {} + virtual std::string operator()(const std::string &matId, + std::vector &materials, + std::map &matMap); - private: - std::string m_mtlBasePath; +private: + std::string m_mtlBasePath; }; /// Loads .obj from a file. @@ -81,27 +74,21 @@ class MaterialFileReader: /// The function returns error string. /// Returns empty string when loading .obj success. /// 'mtl_basepath' is optional, and used for base path for .mtl file. -std::string LoadObj( - std::vector& shapes, // [output] - std::vector& materials, // [output] - const char* filename, - const char* mtl_basepath = NULL); +std::string LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + const char *filename, const char *mtl_basepath = NULL); /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. /// Returns empty string when loading .obj success. -std::string LoadObj( - std::vector& shapes, // [output] - std::vector& materials, // [output] - std::istream& inStream, - MaterialReader& readMatFn); +std::string LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::istream &inStream, MaterialReader &readMatFn); /// Loads materials into std::map /// Returns an empty string if successful -std::string LoadMtl ( - std::map& material_map, - std::vector& materials, - std::istream& inStream); +std::string LoadMtl(std::map &material_map, + std::vector &materials, std::istream &inStream); } -#endif // _TINY_OBJ_LOADER_H +#endif // _TINY_OBJ_LOADER_H From 1390f7f707c6cd76e11f97ba2b182091f2a7f7d5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 15 Feb 2015 17:05:57 +0900 Subject: [PATCH 021/585] Fix compilation. --- tiny_obj_loader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 05c5aad1..6cf3f598 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -210,7 +210,7 @@ static bool exportFaceGroupToShape( const std::vector &in_positions, const std::vector &in_normals, const std::vector &in_texcoords, - const std::vector> &faceGroup, + const std::vector > &faceGroup, const int material_id, const std::string &name, bool clearCache) { if (faceGroup.empty()) { return false; @@ -501,7 +501,7 @@ std::string LoadObj(std::vector &shapes, std::vector v; std::vector vn; std::vector vt; - std::vector> faceGroup; + std::vector > faceGroup; std::string name; // material From 2cceb532148df9a5c375e5e02d0e28268162b46f Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 16 Feb 2015 00:47:23 +0900 Subject: [PATCH 022/585] Update drone.yml. --- .drone.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.drone.yml b/.drone.yml index 7fe6321e..e64ba07b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,6 +4,8 @@ script: - chmod +x ./premake4 - ./premake4 gmake - make +services: + - redis notify: email: recipients: From f0fdaa307dd180076f5862a6585a0eb02bcf334a Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 18 Feb 2015 12:48:50 +0900 Subject: [PATCH 023/585] slight change to fixIndex --- tiny_obj_loader.cc | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 992662ce..6ba4336e 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -66,16 +66,9 @@ static inline bool isNewLine(const char c) { // Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { - int i; - - if (idx > 0) { - i = idx - 1; - } else if (idx == 0) { - i = 0; - } else { // negative value = relative - i = n + idx; - } - return i; + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative } static inline std::string parseString(const char *&token) { From f020169c261bf6ed44ebb9a73d1d4de295aa68e4 Mon Sep 17 00:00:00 2001 From: Simon Otter Date: Mon, 23 Feb 2015 23:34:16 +0100 Subject: [PATCH 024/585] Created a bunch of tests for the parser, and a spec. --- float_parser_tests.cc | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 float_parser_tests.cc diff --git a/float_parser_tests.cc b/float_parser_tests.cc new file mode 100644 index 00000000..e8526ec1 --- /dev/null +++ b/float_parser_tests.cc @@ -0,0 +1,55 @@ +#include +#include + +// Tries to parse a floating point number located at s. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , {digit}] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , decimal , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy, it will parse until it encounters a null-byte or +// a non-conforming character is encountered. +// +// The following situations triggers a failure: +// - parse failure. +// - underflow/overflow. +// +bool tryParseFloat(const char *s, double *result) +{ + return false; +} + +void testParsing(const char *input, bool expectedRes, double expectedValue) +{ + double val = 0.0; + bool res = tryParseFloat(input, &val); + if (res != expectedRes || val != expectedValue) + { + printf("% 20s failed, returned %d and value % 10.4f.\n", input, res, val); + } +} + +int main(int argc, char const *argv[]) +{ + testParsing("0", true, 0.0); + testParsing("-0", true, 0.0); + testParsing("+0", true, 0.0); + testParsing("1", true, 1.0); + testParsing("+1", true, 1.0); + testParsing("-1", true, -1.0); + testParsing("-1.08", true, -1.08); + testParsing("100.0823blabla", true, 100.0823); + testParsing("34.E-2\04", true, 34.0e-2); + testParsing("+34.23E2\02", true, 34.23E2); + return 0; +} \ No newline at end of file From 32414c27b4d4d1819dfefb1fd00bde52aab18673 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 27 Feb 2015 12:07:02 +0900 Subject: [PATCH 025/585] Update TODO. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7c3bb0ae..b72b9117 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ TODO ---- - [ ] Support quad polygon and some tags for OpenSubdiv http://graphics.pixar.com/opensubdiv/ +- [ ] Add rubust parser to replace `atof()` #28 License ------- From 8a384a057bf0d7d49e71292556681f46a9cf57ef Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 27 Feb 2015 12:45:44 +0900 Subject: [PATCH 026/585] Update drone.yml --- .drone.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index e64ba07b..d5c3b4b5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,11 +1,9 @@ -image: bradrydzewski/base +image: syoyo/centos-gcc48 script: - curl -L -o premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true - chmod +x ./premake4 - ./premake4 gmake - make -services: - - redis notify: email: recipients: From 32dcf7d5356fb01b65a7df60022da045a98e13d1 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 27 Feb 2015 15:54:04 +0900 Subject: [PATCH 027/585] Use syoyo/ubu-dev image. --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index d5c3b4b5..971557fd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,4 +1,4 @@ -image: syoyo/centos-gcc48 +image: syoyo/ubu-dev script: - curl -L -o premake4 https://github.com/syoyo/orebuildenv/blob/master/build/linux/bin/premake4?raw=true - chmod +x ./premake4 From 28005f9cdf5894ec2b1dba1b19f497b79d6cdab2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 28 Feb 2015 00:47:13 +0900 Subject: [PATCH 028/585] Update copyright year. --- tiny_obj_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index dbd5f708..512f32ba 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1,5 +1,5 @@ // -// Copyright 2012-2013, Syoyo Fujita. +// Copyright 2012-2015, Syoyo Fujita. // // Licensed under 2-clause BSD liecense. // From daaec1c9aa3448b5c1155bd44a4fcafabb2c133e Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 28 Feb 2015 01:09:18 +0900 Subject: [PATCH 029/585] Small update. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b72b9117..ede414b6 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Features * Texcoord * Normal * Material - * Unknown material attributes are treated as key-value(value is string). + * Unknown material attributes are returned as key-value(value is string) map. Notes ----- From 9d7012673e35128f18734e79c8c4ad3a59adef1e Mon Sep 17 00:00:00 2001 From: Joe Hermaszewski Date: Fri, 27 Feb 2015 19:58:43 +0000 Subject: [PATCH 030/585] Fix a small typo --- tiny_obj_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 6ba4336e..fb780747 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -650,7 +650,7 @@ std::string LoadObj(std::vector &shapes, assert(names.size() > 0); - // names[0] must be 'g', so skipt 0th element. + // names[0] must be 'g', so skip the 0th element. if (names.size() > 1) { name = names[1]; } else { From 4ea1cf0b77a69ccc113a2979db19bcbd32f7ea36 Mon Sep 17 00:00:00 2001 From: Simon Otter Date: Tue, 3 Mar 2015 01:37:28 +0100 Subject: [PATCH 031/585] Implemented a parser and updated tiny_obj_loader.cc to use it unless a define is set. --- float_parser_tests.cc | 200 +++++++++++++++++++++++++++++++++++++++--- tiny_obj_loader.cc | 153 ++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 12 deletions(-) diff --git a/float_parser_tests.cc b/float_parser_tests.cc index e8526ec1..420adea9 100644 --- a/float_parser_tests.cc +++ b/float_parser_tests.cc @@ -1,41 +1,159 @@ #include #include +#include +#include +#include // Tries to parse a floating point number located at s. // +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// // Parses the following EBNF grammar: // sign = "+" | "-" ; // END = ? anything not in digit ? // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; // integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , {digit}] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , decimal , END ) ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; // // Valid strings are for example: -// -0 +3.1417e+2 -0.E-3 1.0324 -1.41 11e2 +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 // // If the parsing is a success, result is set to the parsed value and true // is returned. // -// The function is greedy, it will parse until it encounters a null-byte or -// a non-conforming character is encountered. +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. // // The following situations triggers a failure: +// - s >= s_end. // - parse failure. -// - underflow/overflow. // -bool tryParseFloat(const char *s, double *result) +bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) + { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') + { + sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + goto fail; + } + + // Read the integer part. + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; read++; + } + + // We must make sure we actually got something. + if (read == 0) + goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) + goto assemble; + + // Read the decimal part. + if (*curr == '.') + { + curr++; + read = 1; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * pow(10, -read); + read++; curr++; + } + } + else if (*curr == 'e' || *curr == 'E') {} + else + { + goto assemble; + } + + if (!end_not_reached) + goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') + { + curr++; + // Figure out if a sign is present and if it is. + if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) + { + exp_sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + // Empty E is not allowed. + goto fail; + } + + read = 0; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; read++; + } + exponent *= (exp_sign == '+'? 1 : -1); + if (read == 0) + goto fail; + } + +assemble: + *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5, exponent), exponent); + return true; +fail: return false; } void testParsing(const char *input, bool expectedRes, double expectedValue) { double val = 0.0; - bool res = tryParseFloat(input, &val); - if (res != expectedRes || val != expectedValue) + bool res = tryParseDouble(input, input + strlen(input), &val); + if (res != expectedRes || fabs(val - expectedValue) > 0.000001) { - printf("% 20s failed, returned %d and value % 10.4f.\n", input, res, val); + printf("% 20s failed, returned %d and value % 10.5f.\n\t\tExpected value was % 10.5f\n------\n", input, res, val, expectedValue); } } @@ -49,7 +167,65 @@ int main(int argc, char const *argv[]) testParsing("-1", true, -1.0); testParsing("-1.08", true, -1.08); testParsing("100.0823blabla", true, 100.0823); - testParsing("34.E-2\04", true, 34.0e-2); - testParsing("+34.23E2\02", true, 34.23E2); + testParsing("34.0E-2\04", true, 34.0e-2); + testParsing("+34.23E2\032", true, 34.23E2); + testParsing("10.023", true, 10.023); + testParsing("1.0e+23", true, 1.e+23); + testParsing("10e+23", true, 10e+23); + testParsing("20301.000", true, 20301.000); + testParsing("-41044.000", true, -41044.000); + testParsing("-93558.000", true, -93558.000); + testParsing("-79620.000", true, -79620.000); + testParsing("5257.000", true, 5257.000); + testParsing("-7145.000", true, -7145.000); + testParsing("-77314.000", true, -77314.000); + testParsing("-27131.000", true, -27131.000); + testParsing("52744.000", true, 52744.000); + testParsing("48106.000", true, 48106.000); + testParsing("42939.000", true, 42939.000); + testParsing("41187.000", true, 41187.000); + testParsing("70851.000", true, 70851.000); + testParsing("-90431.000", true, -90431.000); + testParsing("-13564.000", true, -13564.000); + testParsing("27150.000", true, 27150.000); + testParsing("71992.000", true, 71992.000); + testParsing("-42845.000", true, -42845.000); + testParsing("24806.000", true, 24806.000); + testParsing("30753.000", true, 30753.000); + testParsing("1.658500e+04", true, 1.658500e+04); + testParsing("9.572500e+04", true, 9.572500e+04); + testParsing("-5.888600e+04", true, -5.888600e+04); + testParsing("-2.998000e+04", true, -2.998000e+04); + testParsing("-4.842700e+04", true, -4.842700e+04); + testParsing("9.575400e+04", true, 9.575400e+04); + testParsing("-8.311500e+04", true, -8.311500e+04); + testParsing("-3.770800e+04", true, -3.770800e+04); + testParsing("-3.141600e+04", true, -3.141600e+04); + testParsing("-4.673700e+04", true, -4.673700e+04); + testParsing("-5.112400e+04", true, -5.112400e+04); + testParsing("7.111000e+03", true, 7.111000e+03); + testParsing("-3.727000e+03", true, -3.727000e+03); + testParsing("6.940700e+04", true, 6.940700e+04); + testParsing("-3.635100e+04", true, -3.635100e+04); + testParsing("2.762100e+04", true, 2.762100e+04); + testParsing("-1.512600e+04", true, -1.512600e+04); + testParsing("3.338000e+03", true, 3.338000e+03); + testParsing("7.598100e+04", true, 7.598100e+04); + testParsing("-8.198400e+04", true, -8.198400e+04); + testParsing("-", false, 0.0); + testParsing(" +", false, 0.0); + testParsing("", false, 0.0); + testParsing(".232", false, 0.0); + testParsing(".232E2", false, 0.0); + testParsing(".232", false, 0.0); + testParsing(".", false, 0.0); + testParsing(".E", false, 0.0); + testParsing("233.E", false, 0.0); + testParsing("233.323E-", false, 0.0); + testParsing("233.323E+", false, 0.0); + + + + return 0; } \ No newline at end of file diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 6ba4336e..8dd5422e 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -87,13 +88,165 @@ static inline int parseInt(const char *&token) { return i; } + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) +{ + if (s >= s_end) + { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') + { + sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + goto fail; + } + + // Read the integer part. + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; read++; + } + + // We must make sure we actually got something. + if (read == 0) + goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) + goto assemble; + + // Read the decimal part. + if (*curr == '.') + { + curr++; + read = 1; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * pow(10, -read); + read++; curr++; + } + } + else if (*curr == 'e' || *curr == 'E') {} + else + { + goto assemble; + } + + if (!end_not_reached) + goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') + { + curr++; + // Figure out if a sign is present and if it is. + if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) + { + exp_sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + // Empty E is not allowed. + goto fail; + } + + read = 0; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; read++; + } + exponent *= (exp_sign == '+'? 1 : -1); + if (read == 0) + goto fail; + } + +assemble: + *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5, exponent), exponent); + return true; +fail: + return false; +} static inline float parseFloat(const char *&token) { token += strspn(token, " \t"); +#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER float f = (float)atof(token); token += strcspn(token, " \t\r"); +#else + const char *end = token + strcspn(token, " \t\r"); + double val = 0.0; + tryParseDouble(token, end, &val); + float f = static_cast(val); + token = end; +#endif return f; } + static inline void parseFloat2(float &x, float &y, const char *&token) { x = parseFloat(token); y = parseFloat(token); From 5615af531640a6a5b0004430b1497775c6030c35 Mon Sep 17 00:00:00 2001 From: Simon Otter Date: Tue, 3 Mar 2015 01:39:38 +0100 Subject: [PATCH 032/585] Removed stray parser test file. --- float_parser_tests.cc | 231 ------------------------------------------ 1 file changed, 231 deletions(-) delete mode 100644 float_parser_tests.cc diff --git a/float_parser_tests.cc b/float_parser_tests.cc deleted file mode 100644 index 420adea9..00000000 --- a/float_parser_tests.cc +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include -#include -#include - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -bool tryParseDouble(const char *s, const char *s_end, double *result) -{ - if (s >= s_end) - { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') - { - sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - goto fail; - } - - // Read the integer part. - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; read++; - } - - // We must make sure we actually got something. - if (read == 0) - goto fail; - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) - goto assemble; - - // Read the decimal part. - if (*curr == '.') - { - curr++; - read = 1; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * pow(10, -read); - read++; curr++; - } - } - else if (*curr == 'e' || *curr == 'E') {} - else - { - goto assemble; - } - - if (!end_not_reached) - goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') - { - curr++; - // Figure out if a sign is present and if it is. - if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) - { - exp_sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - // Empty E is not allowed. - goto fail; - } - - read = 0; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; read++; - } - exponent *= (exp_sign == '+'? 1 : -1); - if (read == 0) - goto fail; - } - -assemble: - *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5, exponent), exponent); - return true; -fail: - return false; -} - -void testParsing(const char *input, bool expectedRes, double expectedValue) -{ - double val = 0.0; - bool res = tryParseDouble(input, input + strlen(input), &val); - if (res != expectedRes || fabs(val - expectedValue) > 0.000001) - { - printf("% 20s failed, returned %d and value % 10.5f.\n\t\tExpected value was % 10.5f\n------\n", input, res, val, expectedValue); - } -} - -int main(int argc, char const *argv[]) -{ - testParsing("0", true, 0.0); - testParsing("-0", true, 0.0); - testParsing("+0", true, 0.0); - testParsing("1", true, 1.0); - testParsing("+1", true, 1.0); - testParsing("-1", true, -1.0); - testParsing("-1.08", true, -1.08); - testParsing("100.0823blabla", true, 100.0823); - testParsing("34.0E-2\04", true, 34.0e-2); - testParsing("+34.23E2\032", true, 34.23E2); - testParsing("10.023", true, 10.023); - testParsing("1.0e+23", true, 1.e+23); - testParsing("10e+23", true, 10e+23); - testParsing("20301.000", true, 20301.000); - testParsing("-41044.000", true, -41044.000); - testParsing("-93558.000", true, -93558.000); - testParsing("-79620.000", true, -79620.000); - testParsing("5257.000", true, 5257.000); - testParsing("-7145.000", true, -7145.000); - testParsing("-77314.000", true, -77314.000); - testParsing("-27131.000", true, -27131.000); - testParsing("52744.000", true, 52744.000); - testParsing("48106.000", true, 48106.000); - testParsing("42939.000", true, 42939.000); - testParsing("41187.000", true, 41187.000); - testParsing("70851.000", true, 70851.000); - testParsing("-90431.000", true, -90431.000); - testParsing("-13564.000", true, -13564.000); - testParsing("27150.000", true, 27150.000); - testParsing("71992.000", true, 71992.000); - testParsing("-42845.000", true, -42845.000); - testParsing("24806.000", true, 24806.000); - testParsing("30753.000", true, 30753.000); - testParsing("1.658500e+04", true, 1.658500e+04); - testParsing("9.572500e+04", true, 9.572500e+04); - testParsing("-5.888600e+04", true, -5.888600e+04); - testParsing("-2.998000e+04", true, -2.998000e+04); - testParsing("-4.842700e+04", true, -4.842700e+04); - testParsing("9.575400e+04", true, 9.575400e+04); - testParsing("-8.311500e+04", true, -8.311500e+04); - testParsing("-3.770800e+04", true, -3.770800e+04); - testParsing("-3.141600e+04", true, -3.141600e+04); - testParsing("-4.673700e+04", true, -4.673700e+04); - testParsing("-5.112400e+04", true, -5.112400e+04); - testParsing("7.111000e+03", true, 7.111000e+03); - testParsing("-3.727000e+03", true, -3.727000e+03); - testParsing("6.940700e+04", true, 6.940700e+04); - testParsing("-3.635100e+04", true, -3.635100e+04); - testParsing("2.762100e+04", true, 2.762100e+04); - testParsing("-1.512600e+04", true, -1.512600e+04); - testParsing("3.338000e+03", true, 3.338000e+03); - testParsing("7.598100e+04", true, 7.598100e+04); - testParsing("-8.198400e+04", true, -8.198400e+04); - testParsing("-", false, 0.0); - testParsing(" +", false, 0.0); - testParsing("", false, 0.0); - testParsing(".232", false, 0.0); - testParsing(".232E2", false, 0.0); - testParsing(".232", false, 0.0); - testParsing(".", false, 0.0); - testParsing(".E", false, 0.0); - testParsing("233.E", false, 0.0); - testParsing("233.323E-", false, 0.0); - testParsing("233.323E+", false, 0.0); - - - - - return 0; -} \ No newline at end of file From 8d300917a34fd5d94775b19e826277be601087f8 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 3 Mar 2015 13:16:56 +0900 Subject: [PATCH 033/585] Update README. Bump version 0.9.9. --- README.md | 1 + tiny_obj_loader.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index ede414b6..7de4c3ea 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- +* Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel! * Feb 06, 2015 : Fix parsing multi-material object * Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo! * Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran! diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 70ef1b1f..9cd3fd2d 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,7 @@ // // +// version 0.9.9: Replace atof() with custom parser. // version 0.9.8: Fix multi-materials(per-face material ID). // version 0.9.7: Support multi-materials(per-face material ID) per // object/group. From ba5fde9fd59ce9b36b492ad6c6cfd4c82749d763 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 3 Mar 2015 13:22:56 +0900 Subject: [PATCH 034/585] Remove #28 TODO. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7de4c3ea..4d194e84 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ TODO ---- - [ ] Support quad polygon and some tags for OpenSubdiv http://graphics.pixar.com/opensubdiv/ -- [ ] Add rubust parser to replace `atof()` #28 License ------- From 527000abd633f1ce838feae4a5b58526bcf9a4c9 Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 1 May 2015 17:32:25 -0600 Subject: [PATCH 035/585] Add support for referencing multiple mtllibs --- tiny_obj_loader.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 9cd3fd2d..8fe42c82 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -406,7 +406,6 @@ static bool exportFaceGroupToShape( std::string LoadMtl(std::map &material_map, std::vector &materials, std::istream &inStream) { - material_map.clear(); std::stringstream err; material_t material; From d828e7521d03884ee8251e3c418a522a4775e8e8 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 21 May 2015 22:22:59 +0200 Subject: [PATCH 036/585] Add CMake options to toggle the TestLoader application and OBJ Sticher application so that including the project has a smaller footprint --- CMakeLists.txt | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad1b8dfb..4db707c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,18 +26,26 @@ add_library(tinyobjloader ${tinyobjloader-Source} ) -add_executable(test_loader ${tinyobjloader-Test-Source}) -target_link_libraries(test_loader tinyobjloader) +option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Test Loader Application" OFF) -add_executable(obj_sticher ${tinyobjloader-examples-objsticher}) -target_link_libraries(obj_sticher tinyobjloader) +if(TINYOBJLOADER_BUILD_TEST_LOADER) + add_executable(test_loader ${tinyobjloader-Test-Source}) + target_link_libraries(test_loader tinyobjloader) +endif() + +option(TINYOBJLOADER_BUILD_OBJ_STICHER "Build OBJ Sticher Application" OFF) +if (TINYOBJLOADER_BUILD_OBJ_STICHER) + add_executable(obj_sticher ${tinyobjloader-examples-objsticher}) + target_link_libraries(obj_sticher tinyobjloader) + + install ( TARGETS + obj_sticher + DESTINATION + bin + ) +endif() #Installation -install ( TARGETS - obj_sticher - DESTINATION - bin - ) install ( TARGETS tinyobjloader DESTINATION From 49e82e2e00057fe881ba8cff33ebc4f210b32401 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:13:00 +0200 Subject: [PATCH 037/585] Fix compile warnings under VS 2013 /W4 warning level --- tiny_obj_loader.cc | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 8fe42c82..d9723e3e 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -76,7 +76,7 @@ static inline int fixIndex(int idx, int n) { static inline std::string parseString(const char *&token) { std::string s; token += strspn(token, " \t"); - int e = strcspn(token, " \t\r"); + size_t e = strcspn(token, " \t\r"); s = std::string(token, &token[e]); token += e; return s; @@ -325,7 +325,7 @@ updateVertex(std::map &vertexCache, texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]); } - unsigned int idx = positions.size() / 3 - 1; + unsigned int idx = static_cast(positions.size() / 3 - 1); vertexCache[i] = idx; return idx; @@ -448,7 +448,7 @@ std::string LoadMtl(std::map &material_map, // flush previous material. if (!material.name.empty()) { material_map.insert( - std::pair(material.name, materials.size())); + std::pair(material.name, static_cast(materials.size()))); materials.push_back(material); } @@ -458,7 +458,11 @@ std::string LoadMtl(std::map &material_map, // set new mtl name char namebuf[4096]; token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf); +#else sscanf(token, "%s", namebuf); +#endif material.name = namebuf; continue; } @@ -585,7 +589,7 @@ std::string LoadMtl(std::map &material_map, _space = strchr(token, '\t'); } if (_space) { - int len = _space - token; + ptrdiff_t len = _space - token; std::string key(token, len); std::string value = _space + 1; material.unknown_parameter.insert( @@ -594,7 +598,7 @@ std::string LoadMtl(std::map &material_map, } // flush last material. material_map.insert( - std::pair(material.name, materials.size())); + std::pair(material.name, static_cast(materials.size()))); materials.push_back(material); return err.str(); @@ -729,9 +733,9 @@ std::string LoadObj(std::vector &shapes, std::vector face; while (!isNewLine(token[0])) { vertex_index vi = - parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2); + parseTriple(token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); face.push_back(vi); - int n = strspn(token, " \t\r"); + size_t n = strspn(token, " \t\r"); token += n; } @@ -745,7 +749,11 @@ std::string LoadObj(std::vector &shapes, char namebuf[4096]; token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf); +#else sscanf(token, "%s", namebuf); +#endif // Create face group per material. bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, @@ -768,7 +776,11 @@ std::string LoadObj(std::vector &shapes, if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { char namebuf[4096]; token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf); +#else sscanf(token, "%s", namebuf); +#endif std::string err_mtl = readMatFn(namebuf, materials, material_map); if (!err_mtl.empty()) { @@ -830,7 +842,11 @@ std::string LoadObj(std::vector &shapes, // @todo { multiple object name? } char namebuf[4096]; token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf); +#else sscanf(token, "%s", namebuf); +#endif name = std::string(namebuf); continue; From 0aab63eb2046b1be0ea549d8959eef5c1a11e464 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 23 May 2015 22:15:23 +0200 Subject: [PATCH 038/585] Fixing ptrdiff_t compile error --- tiny_obj_loader.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index d9723e3e..ca5222ac 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -589,7 +590,7 @@ std::string LoadMtl(std::map &material_map, _space = strchr(token, '\t'); } if (_space) { - ptrdiff_t len = _space - token; + std::ptrdiff_t len = _space - token; std::string key(token, len); std::string value = _space + 1; material.unknown_parameter.insert( From 42d6bfbafb656c969935d0a1fd47099cb65a433c Mon Sep 17 00:00:00 2001 From: MutterOberin Date: Mon, 1 Jun 2015 16:39:36 +0200 Subject: [PATCH 039/585] Changed pow function to use double overload --- tiny_obj_loader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index ca5222ac..b74b9412 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -185,7 +185,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * pow(10, -read); + mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); read++; curr++; } } @@ -228,7 +228,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) } assemble: - *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5, exponent), exponent); + *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); return true; fail: return false; From a7759a740a223343bad8c6fc4fc714ba0581e60c Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 4 Jun 2015 00:01:51 +0900 Subject: [PATCH 040/585] Add Appveyor settings. --- appveyor.yml | 12 ++++++++++++ tools/windows/premake5.exe | Bin 0 -> 514560 bytes vcsetup.bat | 1 + 3 files changed, 13 insertions(+) create mode 100644 appveyor.yml create mode 100644 tools/windows/premake5.exe create mode 100644 vcsetup.bat diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..38eaf158 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,12 @@ +version: 0.9.{build} + +# scripts that runs after repo cloning. +install: + - vcsetup.bat + +platform: x64 +configuration: Release + +build: + parallel: true + project: TinyObjLoaderSolution.sln diff --git a/tools/windows/premake5.exe b/tools/windows/premake5.exe new file mode 100644 index 0000000000000000000000000000000000000000..c0bf928d0ab4b377eed48ef1d0162e97d5d1eaeb GIT binary patch literal 514560 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,jV*)uZk&RJ9MdBa9i6-or}V*qO~P+QuGw>QqRf) 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%0D`kMbsU`+gzZ=vGyUm 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�&r14gN%}@g_?|@iABl9X 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 literal 0 HcmV?d00001 diff --git a/vcsetup.bat b/vcsetup.bat new file mode 100644 index 00000000..921c1e9e --- /dev/null +++ b/vcsetup.bat @@ -0,0 +1 @@ +.\\tools\\windows\\premake5.exe vs2013 From 4e9e812b096672612981e772311b8d0870e1fa0a Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 4 Jun 2015 00:06:56 +0900 Subject: [PATCH 041/585] Add Appveyor batch. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4d194e84..c290cac3 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ tinyobjloader [![wercker status](https://app.wercker.com/status/495a3bac400212cdacdeb4dd9397bf4f/m "wercker status")](https://app.wercker.com/project/bykey/495a3bac400212cdacdeb4dd9397bf4f) +[![Build status](https://ci.appveyor.com/api/projects/status/tlb421q3t2oyobcn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader/branch/master) + http://syoyo.github.io/tinyobjloader/ Tiny but poweful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. From a2851fdf176ccc6c544074a6b139b662750a4854 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 8 Jun 2015 14:18:35 +0900 Subject: [PATCH 042/585] Add more links to project using tinyobjloader. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c290cac3..b49a7d7a 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ TinyObjLoader is successfully used in ... * mallie https://lighttransport.github.io/mallie * IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ * Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf +* Awesome Bump http://awesomebump.besaba.com/about/ +* sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront * Your project here! Features From 1adfc794ae3fb11875ed34aa97ec39e5d27fc7d4 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 9 Jun 2015 23:49:52 +0900 Subject: [PATCH 043/585] Fix sscanf_s seg fault on windows. Fixes #41 --- tiny_obj_loader.cc | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index b74b9412..3f4cfc5f 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,19 +5,20 @@ // // -// version 0.9.9: Replace atof() with custom parser. -// version 0.9.8: Fix multi-materials(per-face material ID). -// version 0.9.7: Support multi-materials(per-face material ID) per +// version 0.9.10: Fix seg fault on windows. +// version 0.9.9 : Replace atof() with custom parser. +// version 0.9.8 : Fix multi-materials(per-face material ID). +// version 0.9.7 : Support multi-materials(per-face material ID) per // object/group. -// version 0.9.6: Support Ni(index of refraction) mtl parameter. -// Parse transmittance material parameter correctly. -// version 0.9.5: Parse multiple group name. -// Add support of specifying the base path to load material file. -// version 0.9.4: Initial suupport of group tag(g) -// version 0.9.3: Fix parsing triple 'x/y/z' -// version 0.9.2: Add more .mtl load support -// version 0.9.1: Add initial .mtl load support -// version 0.9.0: Initial +// version 0.9.6 : Support Ni(index of refraction) mtl parameter. +// Parse transmittance material parameter correctly. +// version 0.9.5 : Parse multiple group name. +// Add support of specifying the base path to load material file. +// version 0.9.4 : Initial suupport of group tag(g) +// version 0.9.3 : Fix parsing triple 'x/y/z' +// version 0.9.2 : Add more .mtl load support +// version 0.9.1 : Add initial .mtl load support +// version 0.9.0 : Initial // #include @@ -36,6 +37,8 @@ namespace tinyobj { +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) + struct vertex_index { int v_idx, vt_idx, vn_idx; vertex_index(){}; @@ -457,10 +460,10 @@ std::string LoadMtl(std::map &material_map, InitMaterial(material); // set new mtl name - char namebuf[4096]; + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf); + sscanf_s(token, "%s", namebuf, _countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -748,10 +751,10 @@ std::string LoadObj(std::vector &shapes, // use mtl if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { - char namebuf[4096]; + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf); + sscanf_s(token, "%s", namebuf, _countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -775,10 +778,10 @@ std::string LoadObj(std::vector &shapes, // load mtl if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { - char namebuf[4096]; + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf); + sscanf_s(token, "%s", namebuf, _countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -841,10 +844,10 @@ std::string LoadObj(std::vector &shapes, shape = shape_t(); // @todo { multiple object name? } - char namebuf[4096]; + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 2; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf); + sscanf_s(token, "%s", namebuf, _countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif From 805bd814faf62583ae54091f28eaba0bfb8e7f56 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 20 Jun 2015 19:41:41 +0900 Subject: [PATCH 044/585] Invert 'Tr'. Fixes #43. --- tiny_obj_loader.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 3f4cfc5f..323573d8 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -555,7 +555,8 @@ std::string LoadMtl(std::map &material_map, } if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { token += 2; - material.dissolve = parseFloat(token); + // Invert value of Tr(assume Tr is in range [0, 1]) + material.dissolve = 1.0 - parseFloat(token); continue; } From fb361547e577c5a0b8ba339476716cad9c721034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Sa=CC=81nchez-Sa=CC=81ez?= Date: Mon, 22 Jun 2015 16:14:16 +0100 Subject: [PATCH 045/585] Fix groups being ignored if they have 'usemtl' just before 'g' --- tiny_obj_loader.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 323573d8..5f6359d6 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -764,8 +764,10 @@ std::string LoadObj(std::vector &shapes, bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, material, name, true); if (ret) { - faceGroup.clear(); + shapes.push_back(shape); } + shape = shape_t(); + faceGroup.clear(); if (material_map.find(namebuf) != material_map.end()) { material = material_map[namebuf]; From 3058419d7d5564b03c374f8975fa68281ee6471a Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 23 Jun 2015 16:18:29 +0900 Subject: [PATCH 046/585] Update README. Add bugfix notes. --- README.md | 2 ++ tiny_obj_loader.cc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index b49a7d7a..8154958e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- +* Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors! * Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel! * Feb 06, 2015 : Fix parsing multi-material object * Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo! @@ -47,6 +48,7 @@ TinyObjLoader is successfully used in ... * Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf * Awesome Bump http://awesomebump.besaba.com/about/ * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront +* pbrt-v3 https://https://github.com/mmp/pbrt-v3 * Your project here! Features diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 5f6359d6..93218e9a 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,8 @@ // // +// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) +// version 0.9.11: Invert `Tr` parameter(#43) // version 0.9.10: Fix seg fault on windows. // version 0.9.9 : Replace atof() with custom parser. // version 0.9.8 : Fix multi-materials(per-face material ID). From 164c152216f75a3d9c1e9b4f367118e9a9343180 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 25 Jun 2015 20:24:24 +0900 Subject: [PATCH 047/585] Initialized a material. Add warning message to `err` when material file not found. --- tiny_obj_loader.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 93218e9a..3c31b026 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -414,7 +414,9 @@ std::string LoadMtl(std::map &material_map, std::istream &inStream) { std::stringstream err; + // Create a default material anyway. material_t material; + InitMaterial(material); int maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. @@ -623,7 +625,13 @@ std::string MaterialFileReader::operator()(const std::string &matId, } std::ifstream matIStream(filepath.c_str()); - return LoadMtl(matMap, materials, matIStream); + std::string err = LoadMtl(matMap, materials, matIStream); + if (!matIStream) { + std::stringstream ss; + ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; + err += ss.str(); + } + return err; } std::string LoadObj(std::vector &shapes, From 82ae20b8339152bb0379a520f766405a815e7903 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 25 Jun 2015 20:32:46 +0900 Subject: [PATCH 048/585] +1 version num. --- tiny_obj_loader.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 3c31b026..57596dca 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,7 @@ // // +// version 0.9.13: Report "Material file not found message" in `err`(#46) // version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) // version 0.9.11: Invert `Tr` parameter(#43) // version 0.9.10: Fix seg fault on windows. From def9fe7f16e2d24d0150c2cec80a5ec7b168bd68 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Thu, 16 Jul 2015 16:02:50 +0000 Subject: [PATCH 049/585] Added Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8154958e..6ee59f60 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ tinyobjloader ============= +[![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![wercker status](https://app.wercker.com/status/495a3bac400212cdacdeb4dd9397bf4f/m "wercker status")](https://app.wercker.com/project/bykey/495a3bac400212cdacdeb4dd9397bf4f) [![Build status](https://ci.appveyor.com/api/projects/status/tlb421q3t2oyobcn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader/branch/master) From aa07206fc170ebc3161638e7dea7621a3b4db4e3 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 24 Jul 2015 11:46:30 +0900 Subject: [PATCH 050/585] Suppress double -> float conversion warning. Fixes #50 --- tiny_obj_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 57596dca..48ecd90f 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -561,7 +561,7 @@ std::string LoadMtl(std::map &material_map, if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { token += 2; // Invert value of Tr(assume Tr is in range [0, 1]) - material.dissolve = 1.0 - parseFloat(token); + material.dissolve = 1.0f - parseFloat(token); continue; } From 870ead273e106e3aac6de2f5b1712ce1a3d9abfa Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 4 Aug 2015 14:27:40 +0900 Subject: [PATCH 051/585] Initial support of tinyobjloader on Android NDK platform(NDK r10 confirmed to be able to compile). --- jni/Android.mk | 12 ++++++++++++ jni/Application.mk | 2 ++ jni/Makefile | 2 ++ jni/README | 1 + tiny_obj_loader.cc | 1 + 5 files changed, 18 insertions(+) create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/Makefile create mode 100644 jni/README diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 00000000..eaed1b78 --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,12 @@ +# A simple test for the minimal standard C++ library +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := tinyobjloader +LOCAL_SRC_FILES := ../tiny_obj_loader.cc ../test.cc + +LOCAL_C_INCLUDES := ../ + +include $(BUILD_EXECUTABLE) diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 00000000..e5d31919 --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,2 @@ +APP_ABI := all +APP_STL := stlport_static diff --git a/jni/Makefile b/jni/Makefile new file mode 100644 index 00000000..1f138538 --- /dev/null +++ b/jni/Makefile @@ -0,0 +1,2 @@ +all: + ndk-build diff --git a/jni/README b/jni/README new file mode 100644 index 00000000..f93bc08c --- /dev/null +++ b/jni/README @@ -0,0 +1 @@ +Just tests compilation with Android NDK r10. diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 48ecd90f..7d64e460 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include From 191a7dfdc88a5fb960dfee13af6d8230a3e0fbcd Mon Sep 17 00:00:00 2001 From: Nicolas Guillemot Date: Sat, 8 Aug 2015 16:20:57 -0700 Subject: [PATCH 052/585] fix sscanf_s buffer size type Uses of sscanf_s give the following warnings in 64-bit builds: tiny_obj_loader.cc(471): warning C4477: 'sscanf_s' : format string '%s' requires an argument of type 'int', but variadic argument 2 has type 'unsigned __int64' tiny_obj_loader.cc(471): note: this argument is used as a buffer size This was fixed by casting the uses of _countof(namebuf) to unsigned, since the MSDN documentation for sscanf_s specifies that "The size parameter is of type **unsigned**, not **size_t**." (https://msdn.microsoft.com/en-us/library/t6z7bya3.aspx) This silences the warnings. --- tiny_obj_loader.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 7d64e460..e9cae10f 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -469,7 +469,7 @@ std::string LoadMtl(std::map &material_map, char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, _countof(namebuf)); + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -767,7 +767,7 @@ std::string LoadObj(std::vector &shapes, char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, _countof(namebuf)); + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -796,7 +796,7 @@ std::string LoadObj(std::vector &shapes, char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, _countof(namebuf)); + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif @@ -862,7 +862,7 @@ std::string LoadObj(std::vector &shapes, char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 2; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, _countof(namebuf)); + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); #else sscanf(token, "%s", namebuf); #endif From 7f2092b29f80fbdb4fb0b43c225a7b0eca12ecd5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 1 Sep 2015 20:20:10 +0900 Subject: [PATCH 053/585] Support specular highlight, bump, displacement and alpha texture(Remove non-standard "normal map"). Fixes #53. --- test.cc | 5 ++++- tiny_obj_loader.cc | 38 +++++++++++++++++++++++++++++++++++--- tiny_obj_loader.h | 11 +++++++---- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/test.cc b/test.cc index 1ad6d8c1..3f16e3e7 100644 --- a/test.cc +++ b/test.cc @@ -45,7 +45,10 @@ static void PrintInfo(const std::vector& shapes, const std::ve printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", materials[i].normal_texname.c_str()); + printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); + printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); + printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); + printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); std::map::const_iterator it(materials[i].unknown_parameter.begin()); std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); for (; it != itEnd; it++) { diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index e9cae10f..4d6fc95a 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,7 @@ // // +// version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) // version 0.9.13: Report "Material file not found message" in `err`(#46) // version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) // version 0.9.11: Invert `Tr` parameter(#43) @@ -344,7 +345,10 @@ void InitMaterial(material_t &material) { material.ambient_texname = ""; material.diffuse_texname = ""; material.specular_texname = ""; - material.normal_texname = ""; + material.specular_highlight_texname = ""; + material.bump_texname = ""; + material.displacement_texname = ""; + material.alpha_texname = ""; for (int i = 0; i < 3; i++) { material.ambient[i] = 0.f; material.diffuse[i] = 0.f; @@ -587,10 +591,38 @@ std::string LoadMtl(std::map &material_map, continue; } - // normal texture + // specular highlight texture if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { token += 7; - material.normal_texname = token; + material.specular_highlight_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { + token += 9; + material.bump_texname = token; + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { + token += 6; + material.bump_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { + token += 5; + material.bump_texname = token; + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { + token += 5; + material.displacement_texname = token; continue; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 512f32ba..00259e7a 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -26,10 +26,13 @@ typedef struct { // illumination model (see http://www.fileformat.info/format/material/) int illum; - std::string ambient_texname; - std::string diffuse_texname; - std::string specular_texname; - std::string normal_texname; + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d std::map unknown_parameter; } material_t; From 475bc83ef3193198f145896abc864429ef07fdbf Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 2 Sep 2015 18:55:34 +0900 Subject: [PATCH 054/585] Fix wrong texname assignment for `map_d`. --- tiny_obj_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 4d6fc95a..067f6422 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -608,7 +608,7 @@ std::string LoadMtl(std::map &material_map, // alpha texture if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { token += 6; - material.bump_texname = token; + material.alpha_texname = token; continue; } From fcad68bf2d819bd0cdb7975ab956b8cf0f6bfac0 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 21 Oct 2015 03:23:06 +0200 Subject: [PATCH 055/585] update --- python/cornell_box_multimaterial_output.json | 581 ++++++++++++++++++ python/cornell_box_output.json | 601 ------------------- python/howto.py | 4 +- python/main.cpp | 23 +- python/pyTOL.cbp.mak | 96 --- 5 files changed, 599 insertions(+), 706 deletions(-) create mode 100644 python/cornell_box_multimaterial_output.json delete mode 100644 python/cornell_box_output.json delete mode 100644 python/pyTOL.cbp.mak diff --git a/python/cornell_box_multimaterial_output.json b/python/cornell_box_multimaterial_output.json new file mode 100644 index 00000000..ac2de101 --- /dev/null +++ b/python/cornell_box_multimaterial_output.json @@ -0,0 +1,581 @@ +{ + "shapes": { + "back_wall": { + "material_ids": [ + 0.0, + 0.0 + ], + "normals": [], + "positions": [ + 549.5999755859375, + 0.0, + 559.2000122070312, + 0.0, + 0.0, + 559.2000122070312, + 0.0, + 548.7999877929688, + 559.2000122070312, + 556.0, + 548.7999877929688, + 559.2000122070312 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "texcoords": [] + }, + "floor": { + "material_ids": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "normals": [], + "positions": [ + 552.7999877929688, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 559.2000122070312, + 549.5999755859375, + 0.0, + 559.2000122070312, + 290.0, + 0.0, + 114.0, + 240.0, + 0.0, + 272.0, + 82.0, + 0.0, + 225.0, + 130.0, + 0.0, + 65.0, + 472.0, + 0.0, + 406.0, + 314.0, + 0.0, + 456.0, + 265.0, + 0.0, + 296.0, + 423.0, + 0.0, + 247.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 8.0, + 10.0, + 11.0 + ], + "texcoords": [] + }, + "red_wall": { + "material_ids": [ + 1.0, + 1.0 + ], + "normals": [], + "positions": [ + 552.7999877929688, + 0.0, + 0.0, + 549.5999755859375, + 0.0, + 559.2000122070312, + 556.0, + 548.7999877929688, + 559.2000122070312, + 556.0, + 548.7999877929688, + 0.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "texcoords": [] + }, + "light": { + "material_ids": [ + 4.0, + 4.0 + ], + "normals": [], + "positions": [ + 343.0, + 548.0, + 227.0, + 343.0, + 548.0, + 332.0, + 213.0, + 548.0, + 332.0, + 213.0, + 548.0, + 227.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "texcoords": [] + }, + "tall_block": { + "material_ids": [ + 2.0, + 2.0, + 2.0, + 2.0 + ], + "normals": [], + "positions": [ + 314.0, + 0.0, + 456.0, + 314.0, + 330.0, + 456.0, + 265.0, + 330.0, + 296.0, + 265.0, + 0.0, + 296.0, + 265.0, + 0.0, + 296.0, + 265.0, + 330.0, + 296.0, + 423.0, + 330.0, + 247.0, + 423.0, + 0.0, + 247.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0 + ], + "texcoords": [] + }, + "short_block": { + "material_ids": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "normals": [], + "positions": [ + 130.0, + 165.0, + 65.0, + 82.0, + 165.0, + 225.0, + 240.0, + 165.0, + 272.0, + 290.0, + 165.0, + 114.0, + 290.0, + 0.0, + 114.0, + 290.0, + 165.0, + 114.0, + 240.0, + 165.0, + 272.0, + 240.0, + 0.0, + 272.0, + 130.0, + 0.0, + 65.0, + 130.0, + 165.0, + 65.0, + 290.0, + 165.0, + 114.0, + 290.0, + 0.0, + 114.0, + 82.0, + 0.0, + 225.0, + 82.0, + 165.0, + 225.0, + 130.0, + 165.0, + 65.0, + 130.0, + 0.0, + 65.0, + 240.0, + 0.0, + 272.0, + 240.0, + 165.0, + 272.0, + 82.0, + 165.0, + 225.0, + 82.0, + 0.0, + 225.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 4.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, + 8.0, + 10.0, + 11.0, + 12.0, + 13.0, + 14.0, + 12.0, + 14.0, + 15.0, + 16.0, + 17.0, + 18.0, + 16.0, + 18.0, + 19.0 + ], + "texcoords": [] + }, + "green_wall": { + "material_ids": [ + 2.0, + 2.0 + ], + "normals": [], + "positions": [ + 0.0, + 0.0, + 559.2000122070312, + 0.0, + 0.0, + 0.0, + 0.0, + 548.7999877929688, + 0.0, + 0.0, + 548.7999877929688, + 559.2000122070312 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "texcoords": [] + }, + "ceiling": { + "material_ids": [ + 0.0, + 0.0 + ], + "normals": [], + "positions": [ + 556.0, + 548.7999877929688, + 0.0, + 556.0, + 548.7999877929688, + 559.2000122070312, + 0.0, + 548.7999877929688, + 559.2000122070312, + 0.0, + 548.7999877929688, + 0.0 + ], + "indicies": [ + 0.0, + 1.0, + 2.0, + 0.0, + 2.0, + 3.0 + ], + "texcoords": [] + } + }, + "materials": { + "blue": { + "diffuse_texname": "", + "ambient_texname": "", + "illum": 0, + "displacement_texname": "", + "alpha_texname": "", + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "bump_texname": "", + "diffuse": [ + 0.0, + 0.0, + 1.0 + ], + "shininess": 1.0, + "specular_highlight_texname": "", + "unknown_parameter": {}, + "ior": 1.0, + "dissolve": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "" + }, + "white": { + "diffuse_texname": "", + "ambient_texname": "", + "illum": 0, + "displacement_texname": "", + "alpha_texname": "", + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "bump_texname": "", + "diffuse": [ + 1.0, + 1.0, + 1.0 + ], + "shininess": 1.0, + "specular_highlight_texname": "", + "unknown_parameter": {}, + "ior": 1.0, + "dissolve": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "" + }, + "red": { + "diffuse_texname": "", + "ambient_texname": "", + "illum": 0, + "displacement_texname": "", + "alpha_texname": "", + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "bump_texname": "", + "diffuse": [ + 1.0, + 0.0, + 0.0 + ], + "shininess": 1.0, + "specular_highlight_texname": "", + "unknown_parameter": {}, + "ior": 1.0, + "dissolve": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "" + }, + "light": { + "diffuse_texname": "", + "ambient_texname": "", + "illum": 0, + "displacement_texname": "", + "alpha_texname": "", + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "ambient": [ + 20.0, + 20.0, + 20.0 + ], + "bump_texname": "", + "diffuse": [ + 1.0, + 1.0, + 1.0 + ], + "shininess": 1.0, + "specular_highlight_texname": "", + "unknown_parameter": {}, + "ior": 1.0, + "dissolve": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "" + }, + "green": { + "diffuse_texname": "", + "ambient_texname": "", + "illum": 0, + "displacement_texname": "", + "alpha_texname": "", + "emission": [ + 0.0, + 0.0, + 0.0 + ], + "transmittance": [ + 0.0, + 0.0, + 0.0 + ], + "ambient": [ + 0.0, + 0.0, + 0.0 + ], + "bump_texname": "", + "diffuse": [ + 0.0, + 1.0, + 0.0 + ], + "shininess": 1.0, + "specular_highlight_texname": "", + "unknown_parameter": {}, + "ior": 1.0, + "dissolve": 1.0, + "specular": [ + 0.0, + 0.0, + 0.0 + ], + "specular_texname": "" + } + } +} diff --git a/python/cornell_box_output.json b/python/cornell_box_output.json deleted file mode 100644 index ba2e7e1c..00000000 --- a/python/cornell_box_output.json +++ /dev/null @@ -1,601 +0,0 @@ -{ - "shapes": { - "ceiling": { - "texcoords": [], - "positions": [ - 556.0, - 548.7999877929688, - 0.0, - 556.0, - 548.7999877929688, - 559.2000122070312, - 0.0, - 548.7999877929688, - 559.2000122070312, - 0.0, - 548.7999877929688, - 0.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0 - ], - "material_ids": [ - 0.0, - 0.0 - ], - "normals": [] - }, - "floor": { - "texcoords": [], - "positions": [ - 552.7999877929688, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 559.2000122070312, - 549.5999755859375, - 0.0, - 559.2000122070312, - 290.0, - 0.0, - 114.0, - 240.0, - 0.0, - 272.0, - 82.0, - 0.0, - 225.0, - 130.0, - 0.0, - 65.0, - 472.0, - 0.0, - 406.0, - 314.0, - 0.0, - 456.0, - 265.0, - 0.0, - 296.0, - 423.0, - 0.0, - 247.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0, - 4.0, - 5.0, - 6.0, - 4.0, - 6.0, - 7.0, - 8.0, - 9.0, - 10.0, - 8.0, - 10.0, - 11.0 - ], - "material_ids": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "normals": [] - }, - "light": { - "texcoords": [], - "positions": [ - 343.0, - 548.0, - 227.0, - 343.0, - 548.0, - 332.0, - 213.0, - 548.0, - 332.0, - 213.0, - 548.0, - 227.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0 - ], - "material_ids": [ - 4.0, - 4.0 - ], - "normals": [] - }, - "green_wall": { - "texcoords": [], - "positions": [ - 0.0, - 0.0, - 559.2000122070312, - 0.0, - 0.0, - 0.0, - 0.0, - 548.7999877929688, - 0.0, - 0.0, - 548.7999877929688, - 559.2000122070312 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0 - ], - "material_ids": [ - 2.0, - 2.0 - ], - "normals": [] - }, - "back_wall": { - "texcoords": [], - "positions": [ - 549.5999755859375, - 0.0, - 559.2000122070312, - 0.0, - 0.0, - 559.2000122070312, - 0.0, - 548.7999877929688, - 559.2000122070312, - 556.0, - 548.7999877929688, - 559.2000122070312 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0 - ], - "material_ids": [ - 0.0, - 0.0 - ], - "normals": [] - }, - "short_block": { - "texcoords": [], - "positions": [ - 130.0, - 165.0, - 65.0, - 82.0, - 165.0, - 225.0, - 240.0, - 165.0, - 272.0, - 290.0, - 165.0, - 114.0, - 290.0, - 0.0, - 114.0, - 290.0, - 165.0, - 114.0, - 240.0, - 165.0, - 272.0, - 240.0, - 0.0, - 272.0, - 130.0, - 0.0, - 65.0, - 130.0, - 165.0, - 65.0, - 290.0, - 165.0, - 114.0, - 290.0, - 0.0, - 114.0, - 82.0, - 0.0, - 225.0, - 82.0, - 165.0, - 225.0, - 130.0, - 165.0, - 65.0, - 130.0, - 0.0, - 65.0, - 240.0, - 0.0, - 272.0, - 240.0, - 165.0, - 272.0, - 82.0, - 165.0, - 225.0, - 82.0, - 0.0, - 225.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0, - 4.0, - 5.0, - 6.0, - 4.0, - 6.0, - 7.0, - 8.0, - 9.0, - 10.0, - 8.0, - 10.0, - 11.0, - 12.0, - 13.0, - 14.0, - 12.0, - 14.0, - 15.0, - 16.0, - 17.0, - 18.0, - 16.0, - 18.0, - 19.0 - ], - "material_ids": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "normals": [] - }, - "tall_block": { - "texcoords": [], - "positions": [ - 423.0, - 0.0, - 247.0, - 423.0, - 330.0, - 247.0, - 472.0, - 330.0, - 406.0, - 472.0, - 0.0, - 406.0, - 472.0, - 0.0, - 406.0, - 472.0, - 330.0, - 406.0, - 314.0, - 330.0, - 456.0, - 314.0, - 0.0, - 456.0, - 314.0, - 0.0, - 456.0, - 314.0, - 330.0, - 456.0, - 265.0, - 330.0, - 296.0, - 265.0, - 0.0, - 296.0, - 265.0, - 0.0, - 296.0, - 265.0, - 330.0, - 296.0, - 423.0, - 330.0, - 247.0, - 423.0, - 0.0, - 247.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0, - 4.0, - 5.0, - 6.0, - 4.0, - 6.0, - 7.0, - 8.0, - 9.0, - 10.0, - 8.0, - 10.0, - 11.0, - 12.0, - 13.0, - 14.0, - 12.0, - 14.0, - 15.0 - ], - "material_ids": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "normals": [] - }, - "red_wall": { - "texcoords": [], - "positions": [ - 552.7999877929688, - 0.0, - 0.0, - 549.5999755859375, - 0.0, - 559.2000122070312, - 556.0, - 548.7999877929688, - 559.2000122070312, - 556.0, - 548.7999877929688, - 0.0 - ], - "indicies": [ - 0.0, - 1.0, - 2.0, - 0.0, - 2.0, - 3.0 - ], - "material_ids": [ - 1.0, - 1.0 - ], - "normals": [] - } - }, - "materials": { - "blue": { - "transmittance": [ - 0.0, - 0.0, - 0.0 - ], - "illum": 0, - "emission": [ - 0.0, - 0.0, - 0.0 - ], - "diffuse_texname": "", - "ambient_texname": "", - "normal_texname": "", - "shininess": 1.0, - "ior": 1.0, - "specular": [ - 0.0, - 0.0, - 0.0 - ], - "specular_texname": "", - "diffuse": [ - 0.0, - 0.0, - 1.0 - ], - "ambient": [ - 0.0, - 0.0, - 0.0 - ], - "dissolve": 1.0 - }, - "light": { - "transmittance": [ - 0.0, - 0.0, - 0.0 - ], - "illum": 0, - "emission": [ - 0.0, - 0.0, - 0.0 - ], - "diffuse_texname": "", - "ambient_texname": "", - "normal_texname": "", - "shininess": 1.0, - "ior": 1.0, - "specular": [ - 0.0, - 0.0, - 0.0 - ], - "specular_texname": "", - "diffuse": [ - 1.0, - 1.0, - 1.0 - ], - "ambient": [ - 20.0, - 20.0, - 20.0 - ], - "dissolve": 1.0 - }, - "white": { - "transmittance": [ - 0.0, - 0.0, - 0.0 - ], - "illum": 0, - "emission": [ - 0.0, - 0.0, - 0.0 - ], - "diffuse_texname": "", - "ambient_texname": "", - "normal_texname": "", - "shininess": 1.0, - "ior": 1.0, - "specular": [ - 0.0, - 0.0, - 0.0 - ], - "specular_texname": "", - "diffuse": [ - 1.0, - 1.0, - 1.0 - ], - "ambient": [ - 0.0, - 0.0, - 0.0 - ], - "dissolve": 1.0 - }, - "green": { - "transmittance": [ - 0.0, - 0.0, - 0.0 - ], - "illum": 0, - "emission": [ - 0.0, - 0.0, - 0.0 - ], - "diffuse_texname": "", - "ambient_texname": "", - "normal_texname": "", - "shininess": 1.0, - "ior": 1.0, - "specular": [ - 0.0, - 0.0, - 0.0 - ], - "specular_texname": "", - "diffuse": [ - 0.0, - 1.0, - 0.0 - ], - "ambient": [ - 0.0, - 0.0, - 0.0 - ], - "dissolve": 1.0 - }, - "red": { - "transmittance": [ - 0.0, - 0.0, - 0.0 - ], - "illum": 0, - "emission": [ - 0.0, - 0.0, - 0.0 - ], - "diffuse_texname": "", - "ambient_texname": "", - "normal_texname": "", - "shininess": 1.0, - "ior": 1.0, - "specular": [ - 0.0, - 0.0, - 0.0 - ], - "specular_texname": "", - "diffuse": [ - 1.0, - 0.0, - 0.0 - ], - "ambient": [ - 0.0, - 0.0, - 0.0 - ], - "dissolve": 1.0 - } - } -} diff --git a/python/howto.py b/python/howto.py index c5268215..a4c6e04a 100644 --- a/python/howto.py +++ b/python/howto.py @@ -1,11 +1,9 @@ import tinyobjloader as tol import json -model = tol.LoadObj("cornell_box.obj") +model = tol.LoadObj("cornell_box_multimaterial.obj") #print(model["shapes"], model["materials"]) print( json.dumps(model, indent=4) ) #see cornell_box_output.json - - diff --git a/python/main.cpp b/python/main.cpp index b2c6e96f..6ae89cc7 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -31,7 +31,7 @@ extern "C" static PyObject* pyLoadObj(PyObject* self, PyObject* args) { - PyObject *rtndict, *pyshapes, *pymaterials, + PyObject *rtndict, *pyshapes, *pymaterials, *current, *meshobj; char const* filename; @@ -62,7 +62,7 @@ pyLoadObj(PyObject* self, PyObject* args) switch(i) { - case 0: + case 0: current_name = "positions"; vect = vectd(cm.positions.begin(), cm.positions.end()); break; case 1: @@ -77,9 +77,9 @@ pyLoadObj(PyObject* self, PyObject* args) case 4: current_name = "material_ids"; vect = vectd(cm.material_ids.begin(), cm.material_ids.end()); break; - + } - + for (vectd::iterator it = vect.begin() ; it != vect.end(); it++) { @@ -87,7 +87,7 @@ pyLoadObj(PyObject* self, PyObject* args) } PyDict_SetItemString(meshobj, current_name, current); - + } PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj); @@ -97,6 +97,13 @@ pyLoadObj(PyObject* self, PyObject* args) mat != materials.end(); mat++) { PyObject *matobj = PyDict_New(); + PyObject *unknown_parameter = PyDict_New(); + + for (std::map::iterator p = (*mat).unknown_parameter.begin() ; + p != (*mat).unknown_parameter.end(); ++p) + { + PyDict_SetItemString(unknown_parameter, p->first.c_str(), PyUnicode_FromString(p->second.c_str())); + } PyDict_SetItemString(matobj, "shininess", PyFloat_FromDouble((*mat).shininess)); PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior)); @@ -105,12 +112,16 @@ pyLoadObj(PyObject* self, PyObject* args) PyDict_SetItemString(matobj, "ambient_texname", PyUnicode_FromString((*mat).ambient_texname.c_str())); PyDict_SetItemString(matobj, "diffuse_texname", PyUnicode_FromString((*mat).diffuse_texname.c_str())); PyDict_SetItemString(matobj, "specular_texname", PyUnicode_FromString((*mat).specular_texname.c_str())); - PyDict_SetItemString(matobj, "normal_texname", PyUnicode_FromString((*mat).normal_texname.c_str())); + PyDict_SetItemString(matobj, "specular_highlight_texname", PyUnicode_FromString((*mat).specular_highlight_texname.c_str())); + PyDict_SetItemString(matobj, "bump_texname", PyUnicode_FromString((*mat).bump_texname.c_str())); + PyDict_SetItemString(matobj, "displacement_texname", PyUnicode_FromString((*mat).displacement_texname.c_str())); + PyDict_SetItemString(matobj, "alpha_texname", PyUnicode_FromString((*mat).alpha_texname.c_str())); PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient)); PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse)); PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3((*mat).specular)); PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3((*mat).transmittance)); PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3((*mat).emission)); + PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter); PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj); } diff --git a/python/pyTOL.cbp.mak b/python/pyTOL.cbp.mak deleted file mode 100644 index ba18ae99..00000000 --- a/python/pyTOL.cbp.mak +++ /dev/null @@ -1,96 +0,0 @@ -#------------------------------------------------------------------------------# -# This makefile was generated by 'cbp2make' tool rev.147 # -#------------------------------------------------------------------------------# - - -WORKDIR = `pwd` - -CC = gcc -CXX = g++ -AR = ar -LD = g++ -WINDRES = windres - -INC = -CFLAGS = -Wall -fexceptions `python3-config --cflags` -RESINC = -LIBDIR = -LIB = -LDFLAGS = `python3-config --ldflags` - -INC_DEBUG = $(INC) -CFLAGS_DEBUG = $(CFLAGS) -g -RESINC_DEBUG = $(RESINC) -RCFLAGS_DEBUG = $(RCFLAGS) -LIBDIR_DEBUG = $(LIBDIR) -LIB_DEBUG = $(LIB) -LDFLAGS_DEBUG = $(LDFLAGS) -OBJDIR_DEBUG = obj/Debug -DEP_DEBUG = -OUT_DEBUG = bin/Debug/tinyobjloader.so - -INC_RELEASE = $(INC) -CFLAGS_RELEASE = $(CFLAGS) -O2 -RESINC_RELEASE = $(RESINC) -RCFLAGS_RELEASE = $(RCFLAGS) -LIBDIR_RELEASE = $(LIBDIR) -LIB_RELEASE = $(LIB) -LDFLAGS_RELEASE = $(LDFLAGS) -s -OBJDIR_RELEASE = obj/Release -DEP_RELEASE = -OUT_RELEASE = bin/Release/tinyobjloader.so - -OBJ_DEBUG = $(OBJDIR_DEBUG)/main.o $(OBJDIR_DEBUG)/tiny_obj_loader.o - -OBJ_RELEASE = $(OBJDIR_RELEASE)/main.o $(OBJDIR_RELEASE)/tiny_obj_loader.o - -all: debug release - -clean: clean_debug clean_release - -before_debug: - test -d bin/Debug || mkdir -p bin/Debug - test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG) - -after_debug: - -debug: before_debug out_debug after_debug - -out_debug: before_debug $(OBJ_DEBUG) $(DEP_DEBUG) - $(LD) -shared $(LIBDIR_DEBUG) $(OBJ_DEBUG) -o $(OUT_DEBUG) $(LDFLAGS_DEBUG) $(LIB_DEBUG) - -$(OBJDIR_DEBUG)/main.o: main.cpp - $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c main.cpp -o $(OBJDIR_DEBUG)/main.o - -$(OBJDIR_DEBUG)/tiny_obj_loader.o: ../tiny_obj_loader.cc - $(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c ../tiny_obj_loader.cc -o $(OBJDIR_DEBUG)/tiny_obj_loader.o - -clean_debug: - rm -f $(OBJ_DEBUG) $(OUT_DEBUG) - rm -rf bin/Debug - rm -rf $(OBJDIR_DEBUG) - -before_release: - test -d bin/Release || mkdir -p bin/Release - test -d $(OBJDIR_RELEASE) || mkdir -p $(OBJDIR_RELEASE) - -after_release: - -release: before_release out_release after_release - -out_release: before_release $(OBJ_RELEASE) $(DEP_RELEASE) - $(LD) -shared $(LIBDIR_RELEASE) $(OBJ_RELEASE) -o $(OUT_RELEASE) $(LDFLAGS_RELEASE) $(LIB_RELEASE) - -$(OBJDIR_RELEASE)/main.o: main.cpp - $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c main.cpp -o $(OBJDIR_RELEASE)/main.o - -$(OBJDIR_RELEASE)/tiny_obj_loader.o: ../tiny_obj_loader.cc - $(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c ../tiny_obj_loader.cc -o $(OBJDIR_RELEASE)/tiny_obj_loader.o - -clean_release: - rm -f $(OBJ_RELEASE) $(OUT_RELEASE) - rm -rf bin/Release - rm -rf $(OBJDIR_RELEASE) - -.PHONY: before_debug after_debug clean_debug before_release after_release clean_release - From e7e7eed616cee946a10b5239545a75c2c968919e Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 7 Nov 2015 23:08:39 +0900 Subject: [PATCH 056/585] Change API and handle no-mtl-file case correctly. Fixes #58. --- examples/obj_sticher/obj_sticher.cc | 5 +- missing_material_file.obj | 145 ++++++++++++++++++++++++++++ no_material.obj | 133 +++++++++++++++++++++++++ test-nan.obj | 145 ++++++++++++++++++++++++++++ test.cc | 21 +++- tiny_obj_loader.cc | 61 ++++++------ tiny_obj_loader.h | 40 ++++---- 7 files changed, 500 insertions(+), 50 deletions(-) create mode 100644 missing_material_file.obj create mode 100644 no_material.obj create mode 100644 test-nan.obj diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index 8ec7a28f..f59fee4c 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -86,9 +86,12 @@ main( for (int i = 0; i < num_objfiles; i++) { std::cout << "Loading " << argv[i+1] << " ... " << std::flush; - std::string err = tinyobj::LoadObj(shapes[i], materials[i], argv[i+1]); + std::string err; + bool ret = tinyobj::LoadObj(shapes[i], materials[i], err, argv[i+1]); if (!err.empty()) { std::cerr << err << std::endl; + } + if (!ret) { exit(1); } diff --git a/missing_material_file.obj b/missing_material_file.obj new file mode 100644 index 00000000..9e1d98c7 --- /dev/null +++ b/missing_material_file.obj @@ -0,0 +1,145 @@ +# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. +# original cornell box data + # comment + +# empty line including some space + + +#mtllib no_material.mtl + +o floor +usemtl white +v 552.8 0.0 0.0 +v 0.0 0.0 0.0 +v 0.0 0.0 559.2 +v 549.6 0.0 559.2 + +v 130.0 0.0 65.0 +v 82.0 0.0 225.0 +v 240.0 0.0 272.0 +v 290.0 0.0 114.0 + +v 423.0 0.0 247.0 +v 265.0 0.0 296.0 +v 314.0 0.0 456.0 +v 472.0 0.0 406.0 + +f 1 2 3 4 +f 8 7 6 5 +f 12 11 10 9 + +o light +usemtl light +v 343.0 548.0 227.0 +v 343.0 548.0 332.0 +v 213.0 548.0 332.0 +v 213.0 548.0 227.0 +f -4 -3 -2 -1 + +o ceiling +usemtl white +v 556.0 548.8 0.0 +v 556.0 548.8 559.2 +v 0.0 548.8 559.2 +v 0.0 548.8 0.0 +f -4 -3 -2 -1 + +o back_wall +usemtl white +v 549.6 0.0 559.2 +v 0.0 0.0 559.2 +v 0.0 548.8 559.2 +v 556.0 548.8 559.2 +f -4 -3 -2 -1 + +o front_wall +usemtl blue +v 549.6 0.0 0 +v 0.0 0.0 0 +v 0.0 548.8 0 +v 556.0 548.8 0 +#f -1 -2 -3 -4 + +o green_wall +usemtl green +v 0.0 0.0 559.2 +v 0.0 0.0 0.0 +v 0.0 548.8 0.0 +v 0.0 548.8 559.2 +f -4 -3 -2 -1 + +o red_wall +usemtl red +v 552.8 0.0 0.0 +v 549.6 0.0 559.2 +v 556.0 548.8 559.2 +v 556.0 548.8 0.0 +f -4 -3 -2 -1 + +o short_block +usemtl white + +v 130.0 165.0 65.0 +v 82.0 165.0 225.0 +v 240.0 165.0 272.0 +v 290.0 165.0 114.0 +f -4 -3 -2 -1 + +v 290.0 0.0 114.0 +v 290.0 165.0 114.0 +v 240.0 165.0 272.0 +v 240.0 0.0 272.0 +f -4 -3 -2 -1 + +v 130.0 0.0 65.0 +v 130.0 165.0 65.0 +v 290.0 165.0 114.0 +v 290.0 0.0 114.0 +f -4 -3 -2 -1 + +v 82.0 0.0 225.0 +v 82.0 165.0 225.0 +v 130.0 165.0 65.0 +v 130.0 0.0 65.0 +f -4 -3 -2 -1 + +v 240.0 0.0 272.0 +v 240.0 165.0 272.0 +v 82.0 165.0 225.0 +v 82.0 0.0 225.0 +f -4 -3 -2 -1 + +o tall_block +usemtl white + +v 423.0 330.0 247.0 +v 265.0 330.0 296.0 +v 314.0 330.0 456.0 +v 472.0 330.0 406.0 +f -4 -3 -2 -1 + +usemtl white +v 423.0 0.0 247.0 +v 423.0 330.0 247.0 +v 472.0 330.0 406.0 +v 472.0 0.0 406.0 +f -4 -3 -2 -1 + +v 472.0 0.0 406.0 +v 472.0 330.0 406.0 +v 314.0 330.0 456.0 +v 314.0 0.0 456.0 +f -4 -3 -2 -1 + +v 314.0 0.0 456.0 +v 314.0 330.0 456.0 +v 265.0 330.0 296.0 +v 265.0 0.0 296.0 +f -4 -3 -2 -1 + +v 265.0 0.0 296.0 +v 265.0 330.0 296.0 +v 423.0 330.0 247.0 +v 423.0 0.0 247.0 +f -4 -3 -2 -1 + diff --git a/no_material.obj b/no_material.obj new file mode 100644 index 00000000..6f3688f7 --- /dev/null +++ b/no_material.obj @@ -0,0 +1,133 @@ +# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. +# original cornell box data + # comment + +# empty line including some space + + +o floor +v 552.8 0.0 0.0 +v 0.0 0.0 0.0 +v 0.0 0.0 559.2 +v 549.6 0.0 559.2 + +v 130.0 0.0 65.0 +v 82.0 0.0 225.0 +v 240.0 0.0 272.0 +v 290.0 0.0 114.0 + +v 423.0 0.0 247.0 +v 265.0 0.0 296.0 +v 314.0 0.0 456.0 +v 472.0 0.0 406.0 + +f 1 2 3 4 +f 8 7 6 5 +f 12 11 10 9 + +o light +v 343.0 548.0 227.0 +v 343.0 548.0 332.0 +v 213.0 548.0 332.0 +v 213.0 548.0 227.0 +f -4 -3 -2 -1 + +o ceiling +v 556.0 548.8 0.0 +v 556.0 548.8 559.2 +v 0.0 548.8 559.2 +v 0.0 548.8 0.0 +f -4 -3 -2 -1 + +o back_wall +v 549.6 0.0 559.2 +v 0.0 0.0 559.2 +v 0.0 548.8 559.2 +v 556.0 548.8 559.2 +f -4 -3 -2 -1 + +o front_wall +v 549.6 0.0 0 +v 0.0 0.0 0 +v 0.0 548.8 0 +v 556.0 548.8 0 +#f -1 -2 -3 -4 + +o green_wall +v 0.0 0.0 559.2 +v 0.0 0.0 0.0 +v 0.0 548.8 0.0 +v 0.0 548.8 559.2 +f -4 -3 -2 -1 + +o red_wall +v 552.8 0.0 0.0 +v 549.6 0.0 559.2 +v 556.0 548.8 559.2 +v 556.0 548.8 0.0 +f -4 -3 -2 -1 + +o short_block + +v 130.0 165.0 65.0 +v 82.0 165.0 225.0 +v 240.0 165.0 272.0 +v 290.0 165.0 114.0 +f -4 -3 -2 -1 + +v 290.0 0.0 114.0 +v 290.0 165.0 114.0 +v 240.0 165.0 272.0 +v 240.0 0.0 272.0 +f -4 -3 -2 -1 + +v 130.0 0.0 65.0 +v 130.0 165.0 65.0 +v 290.0 165.0 114.0 +v 290.0 0.0 114.0 +f -4 -3 -2 -1 + +v 82.0 0.0 225.0 +v 82.0 165.0 225.0 +v 130.0 165.0 65.0 +v 130.0 0.0 65.0 +f -4 -3 -2 -1 + +v 240.0 0.0 272.0 +v 240.0 165.0 272.0 +v 82.0 165.0 225.0 +v 82.0 0.0 225.0 +f -4 -3 -2 -1 + +o tall_block + +v 423.0 330.0 247.0 +v 265.0 330.0 296.0 +v 314.0 330.0 456.0 +v 472.0 330.0 406.0 +f -4 -3 -2 -1 + +v 423.0 0.0 247.0 +v 423.0 330.0 247.0 +v 472.0 330.0 406.0 +v 472.0 0.0 406.0 +f -4 -3 -2 -1 + +v 472.0 0.0 406.0 +v 472.0 330.0 406.0 +v 314.0 330.0 456.0 +v 314.0 0.0 456.0 +f -4 -3 -2 -1 + +v 314.0 0.0 456.0 +v 314.0 330.0 456.0 +v 265.0 330.0 296.0 +v 265.0 0.0 296.0 +f -4 -3 -2 -1 + +v 265.0 0.0 296.0 +v 265.0 330.0 296.0 +v 423.0 330.0 247.0 +v 423.0 0.0 247.0 +f -4 -3 -2 -1 + diff --git a/test-nan.obj b/test-nan.obj new file mode 100644 index 00000000..3c689250 --- /dev/null +++ b/test-nan.obj @@ -0,0 +1,145 @@ +# cornell_box.obj and cornell_box.mtl are grabbed from Intel's embree project. +# original cornell box data + # comment + +# empty line including some space + + +mtllib cornell_box.mtl + +o floor +usemtl white +v nan -nan nan +v inf -inf inf +v 1.#IND -1.#IND 1.#IND +v 1.#INF -1.#INF 1.#INF + +v 130.0 0.0 65.0 +v 82.0 0.0 225.0 +v 240.0 0.0 272.0 +v 290.0 0.0 114.0 + +v 423.0 0.0 247.0 +v 265.0 0.0 296.0 +v 314.0 0.0 456.0 +v 472.0 0.0 406.0 + +f 1 2 3 4 +f 8 7 6 5 +f 12 11 10 9 + +o light +usemtl light +v 343.0 548.0 227.0 +v 343.0 548.0 332.0 +v 213.0 548.0 332.0 +v 213.0 548.0 227.0 +f -4 -3 -2 -1 + +o ceiling +usemtl white +v 556.0 548.8 0.0 +v 556.0 548.8 559.2 +v 0.0 548.8 559.2 +v 0.0 548.8 0.0 +f -4 -3 -2 -1 + +o back_wall +usemtl white +v 549.6 0.0 559.2 +v 0.0 0.0 559.2 +v 0.0 548.8 559.2 +v 556.0 548.8 559.2 +f -4 -3 -2 -1 + +o front_wall +usemtl blue +v 549.6 0.0 0 +v 0.0 0.0 0 +v 0.0 548.8 0 +v 556.0 548.8 0 +#f -1 -2 -3 -4 + +o green_wall +usemtl green +v 0.0 0.0 559.2 +v 0.0 0.0 0.0 +v 0.0 548.8 0.0 +v 0.0 548.8 559.2 +f -4 -3 -2 -1 + +o red_wall +usemtl red +v 552.8 0.0 0.0 +v 549.6 0.0 559.2 +v 556.0 548.8 559.2 +v 556.0 548.8 0.0 +f -4 -3 -2 -1 + +o short_block +usemtl white + +v 130.0 165.0 65.0 +v 82.0 165.0 225.0 +v 240.0 165.0 272.0 +v 290.0 165.0 114.0 +f -4 -3 -2 -1 + +v 290.0 0.0 114.0 +v 290.0 165.0 114.0 +v 240.0 165.0 272.0 +v 240.0 0.0 272.0 +f -4 -3 -2 -1 + +v 130.0 0.0 65.0 +v 130.0 165.0 65.0 +v 290.0 165.0 114.0 +v 290.0 0.0 114.0 +f -4 -3 -2 -1 + +v 82.0 0.0 225.0 +v 82.0 165.0 225.0 +v 130.0 165.0 65.0 +v 130.0 0.0 65.0 +f -4 -3 -2 -1 + +v 240.0 0.0 272.0 +v 240.0 165.0 272.0 +v 82.0 165.0 225.0 +v 82.0 0.0 225.0 +f -4 -3 -2 -1 + +o tall_block +usemtl white + +v 423.0 330.0 247.0 +v 265.0 330.0 296.0 +v 314.0 330.0 456.0 +v 472.0 330.0 406.0 +f -4 -3 -2 -1 + +usemtl white +v 423.0 0.0 247.0 +v 423.0 330.0 247.0 +v 472.0 330.0 406.0 +v 472.0 0.0 406.0 +f -4 -3 -2 -1 + +v 472.0 0.0 406.0 +v 472.0 330.0 406.0 +v 314.0 330.0 456.0 +v 314.0 0.0 456.0 +f -4 -3 -2 -1 + +v 314.0 0.0 456.0 +v 314.0 330.0 456.0 +v 265.0 330.0 296.0 +v 265.0 0.0 296.0 +f -4 -3 -2 -1 + +v 265.0 0.0 296.0 +v 265.0 330.0 296.0 +v 423.0 330.0 247.0 +v 423.0 0.0 247.0 +f -4 -3 -2 -1 + diff --git a/test.cc b/test.cc index 3f16e3e7..e0126231 100644 --- a/test.cc +++ b/test.cc @@ -67,10 +67,15 @@ TestLoadObj( std::vector shapes; std::vector materials; - std::string err = tinyobj::LoadObj(shapes, materials, filename, basepath); + + std::string err; + bool ret = tinyobj::LoadObj(shapes, materials, err, filename, basepath); if (!err.empty()) { std::cerr << err << std::endl; + } + + if (!ret) { return false; } @@ -152,12 +157,14 @@ std::string matStream( public: MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {} virtual ~MaterialStringStreamReader() {} - virtual std::string operator() ( + virtual bool operator() ( const std::string& matId, std::vector& materials, - std::map& matMap) + std::map& matMap, + std::string& err) { - return LoadMtl(matMap, materials, m_matSStream); + LoadMtl(matMap, materials, m_matSStream); + return true; } private: @@ -167,10 +174,14 @@ std::string matStream( MaterialStringStreamReader matSSReader(matStream); std::vector shapes; std::vector materials; - std::string err = tinyobj::LoadObj(shapes, materials, objStream, matSSReader); + std::string err; + bool ret = tinyobj::LoadObj(shapes, materials, err, objStream, matSSReader); if (!err.empty()) { std::cerr << err << std::endl; + } + + if (!ret) { return false; } diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 067f6422..84c1a77f 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,7 @@ // // +// version 0.9.15: Change API to handle no mtl file case correctly(#58) // version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) // version 0.9.13: Report "Material file not found message" in `err`(#46) // version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) @@ -415,10 +416,9 @@ static bool exportFaceGroupToShape( return true; } -std::string LoadMtl(std::map &material_map, - std::vector &materials, - std::istream &inStream) { - std::stringstream err; +void LoadMtl(std::map &material_map, + std::vector &materials, + std::istream &inStream) { // Create a default material anyway. material_t material; @@ -643,13 +643,12 @@ std::string LoadMtl(std::map &material_map, material_map.insert( std::pair(material.name, static_cast(materials.size()))); materials.push_back(material); - - return err.str(); } -std::string MaterialFileReader::operator()(const std::string &matId, - std::vector &materials, - std::map &matMap) { +bool MaterialFileReader::operator()(const std::string &matId, + std::vector &materials, + std::map &matMap, + std::string& err) { std::string filepath; if (!m_mtlBasePath.empty()) { @@ -659,27 +658,29 @@ std::string MaterialFileReader::operator()(const std::string &matId, } std::ifstream matIStream(filepath.c_str()); - std::string err = LoadMtl(matMap, materials, matIStream); + LoadMtl(matMap, materials, matIStream); if (!matIStream) { std::stringstream ss; ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; err += ss.str(); } - return err; + return true; } -std::string LoadObj(std::vector &shapes, - std::vector &materials, // [output] - const char *filename, const char *mtl_basepath) { +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string &err, + const char *filename, const char *mtl_basepath) { shapes.clear(); - std::stringstream err; + std::stringstream errss; std::ifstream ifs(filename); if (!ifs) { - err << "Cannot open file [" << filename << "]" << std::endl; - return err.str(); + errss << "Cannot open file [" << filename << "]" << std::endl; + err = errss.str(); + return false; } std::string basePath; @@ -688,13 +689,14 @@ std::string LoadObj(std::vector &shapes, } MaterialFileReader matFileReader(basePath); - return LoadObj(shapes, materials, ifs, matFileReader); + return LoadObj(shapes, materials, err, ifs, matFileReader); } -std::string LoadObj(std::vector &shapes, - std::vector &materials, // [output] - std::istream &inStream, MaterialReader &readMatFn) { - std::stringstream err; +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string& err, + std::istream &inStream, MaterialReader &readMatFn) { + std::stringstream errss; std::vector v; std::vector vn; @@ -833,10 +835,13 @@ std::string LoadObj(std::vector &shapes, sscanf(token, "%s", namebuf); #endif - std::string err_mtl = readMatFn(namebuf, materials, material_map); - if (!err_mtl.empty()) { + std::string err_mtl; + bool ok = readMatFn(namebuf, materials, material_map, err_mtl); + err += err_mtl; + + if (!ok) { faceGroup.clear(); // for safety - return err_mtl; + return false; } continue; @@ -913,6 +918,8 @@ std::string LoadObj(std::vector &shapes, } faceGroup.clear(); // for safety - return err.str(); -} + err += errss.str(); + return true; } + +} // namespace diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 00259e7a..0a65b3ad 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -54,9 +54,10 @@ class MaterialReader { MaterialReader() {} virtual ~MaterialReader() {} - virtual std::string operator()(const std::string &matId, - std::vector &materials, - std::map &matMap) = 0; + virtual bool operator()(const std::string &matId, + std::vector &materials, + std::map &matMap, + std::string &err) = 0; }; class MaterialFileReader : public MaterialReader { @@ -64,9 +65,10 @@ class MaterialFileReader : public MaterialReader { MaterialFileReader(const std::string &mtl_basepath) : m_mtlBasePath(mtl_basepath) {} virtual ~MaterialFileReader() {} - virtual std::string operator()(const std::string &matId, - std::vector &materials, - std::map &matMap); + virtual bool operator()(const std::string &matId, + std::vector &materials, + std::map &matMap, + std::string &err); private: std::string m_mtlBasePath; @@ -75,23 +77,27 @@ class MaterialFileReader : public MaterialReader { /// Loads .obj from a file. /// 'shapes' will be filled with parsed shape data /// The function returns error string. -/// Returns empty string when loading .obj success. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` /// 'mtl_basepath' is optional, and used for base path for .mtl file. -std::string LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - const char *filename, const char *mtl_basepath = NULL); +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string& err, // [output] + const char *filename, const char *mtl_basepath = NULL); /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. -/// Returns empty string when loading .obj success. -std::string LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::istream &inStream, MaterialReader &readMatFn); +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string& err, // [output] + std::istream &inStream, MaterialReader &readMatFn); /// Loads materials into std::map -/// Returns an empty string if successful -std::string LoadMtl(std::map &material_map, - std::vector &materials, std::istream &inStream); +void LoadMtl(std::map &material_map, // [output] + std::vector &materials, // [output] + std::istream &inStream); } #endif // _TINY_OBJ_LOADER_H From a13e1d6164a61a57e359aa1f9c6145477b42eda7 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 7 Nov 2015 23:10:42 +0900 Subject: [PATCH 057/585] Update python binding to match new API. --- python/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/main.cpp b/python/main.cpp index 6ae89cc7..f772793c 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -44,7 +44,8 @@ pyLoadObj(PyObject* self, PyObject* args) if(!PyArg_ParseTuple(args, "s", &filename)) return NULL; - tinyobj::LoadObj(shapes, materials, filename); + std::string err; + tinyobj::LoadObj(shapes, materials, err, filename); pyshapes = PyDict_New(); pymaterials = PyDict_New(); From 3681c44aa3a15181b0acde8e5da865fb37eb9e66 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 7 Nov 2015 23:14:12 +0900 Subject: [PATCH 058/585] Update README. --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6ee59f60..10ba57a5 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- +* Nov 08, 2015 : Improved API. * Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors! * Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel! * Feb 06, 2015 : Fix parsing multi-material object @@ -85,10 +86,14 @@ Usage std::vector shapes; std::vector materials; - std::string err = tinyobj::LoadObj(shapes, materials, inputfile.c_str()); + std::string err; + bool ret = tinyobj::LoadObj(shapes, materials, inputfile.c_str()); - if (!err.empty()) { + if (!err.empty()) { // `err` may contain warning message. std::cerr << err << std::endl; + } + + if (!ret) { exit(1); } From c1be55ffe2a6eaa35e54d21ef939590c80f16fda Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 12 Nov 2015 10:50:40 +0900 Subject: [PATCH 059/585] Udate README. Fixes #60. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 10ba57a5..780c24d2 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Usage std::vector materials; std::string err; - bool ret = tinyobj::LoadObj(shapes, materials, inputfile.c_str()); + bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str()); if (!err.empty()) { // `err` may contain warning message. std::cerr << err << std::endl; @@ -133,7 +133,7 @@ Usage printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", materials[i].normal_texname.c_str()); + printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); std::map::const_iterator it(materials[i].unknown_parameter.begin()); std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); for (; it != itEnd; it++) { From ec78aa2bd1a38630fc8de70188a0a53cdaa524ea Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 18 Nov 2015 22:44:18 +0900 Subject: [PATCH 060/585] Add Cocos2d-x. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 780c24d2..69bc8590 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ TinyObjLoader is successfully used in ... * Awesome Bump http://awesomebump.besaba.com/about/ * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront * pbrt-v3 https://https://github.com/mmp/pbrt-v3 +* cocos2d-x https://github.com/cocos2d/cocos2d-x/ * Your project here! Features From 353d527fe8bf9685548f3bb74fdb0f1da5751d10 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 18 Nov 2015 22:44:18 +0900 Subject: [PATCH 061/585] Add Cocos2d-x. Make tinyobjloader header-only. --- README.md | 5 + premake4.lua | 6 +- test.cc | 1 + tiny_obj_loader.cc | 925 +------------------------------------------- tiny_obj_loader.h | 932 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 940 insertions(+), 929 deletions(-) diff --git a/README.md b/README.md index 780c24d2..77dc472c 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- +* Nov 26, 2015 : Now single-header only!. * Nov 08, 2015 : Improved API. * Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors! * Mar 03, 2015 : Replace atof() with hand-written parser for robust reading of numeric value. Thanks skurmedel! @@ -52,6 +53,7 @@ TinyObjLoader is successfully used in ... * Awesome Bump http://awesomebump.besaba.com/about/ * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront * pbrt-v3 https://https://github.com/mmp/pbrt-v3 +* cocos2d-x https://github.com/cocos2d/cocos2d-x/ * Your project here! Features @@ -82,6 +84,9 @@ Licensed under 2 clause BSD. Usage ----- + #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc + #include "tiny_obj_loader.h" + std::string inputfile = "cornell_box.obj"; std::vector shapes; std::vector materials; diff --git a/premake4.lua b/premake4.lua index ad020a66..cbfdfd92 100644 --- a/premake4.lua +++ b/premake4.lua @@ -1,7 +1,3 @@ -lib_sources = { - "tiny_obj_loader.cc" -} - sources = { "test.cc", } @@ -20,7 +16,7 @@ solution "TinyObjLoaderSolution" project "tinyobjloader" kind "ConsoleApp" language "C++" - files { lib_sources, sources } + files { sources } configuration "Debug" defines { "DEBUG" } -- -DDEBUG diff --git a/test.cc b/test.cc index e0126231..46673eb9 100644 --- a/test.cc +++ b/test.cc @@ -1,3 +1,4 @@ +#define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" #include diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 84c1a77f..e57d0444 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -1,925 +1,2 @@ -// -// Copyright 2012-2015, Syoyo Fujita. -// -// Licensed under 2-clause BSD liecense. -// - -// -// version 0.9.15: Change API to handle no mtl file case correctly(#58) -// version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) -// version 0.9.13: Report "Material file not found message" in `err`(#46) -// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) -// version 0.9.11: Invert `Tr` parameter(#43) -// version 0.9.10: Fix seg fault on windows. -// version 0.9.9 : Replace atof() with custom parser. -// version 0.9.8 : Fix multi-materials(per-face material ID). -// version 0.9.7 : Support multi-materials(per-face material ID) per -// object/group. -// version 0.9.6 : Support Ni(index of refraction) mtl parameter. -// Parse transmittance material parameter correctly. -// version 0.9.5 : Parse multiple group name. -// Add support of specifying the base path to load material file. -// version 0.9.4 : Initial suupport of group tag(g) -// version 0.9.3 : Fix parsing triple 'x/y/z' -// version 0.9.2 : Add more .mtl load support -// version 0.9.1 : Add initial .mtl load support -// version 0.9.0 : Initial -// - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - +#define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" - -namespace tinyobj { - -#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) - -struct vertex_index { - int v_idx, vt_idx, vn_idx; - vertex_index(){}; - vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){}; - vertex_index(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){}; -}; -// for std::map -static inline bool operator<(const vertex_index &a, const vertex_index &b) { - if (a.v_idx != b.v_idx) - return (a.v_idx < b.v_idx); - if (a.vn_idx != b.vn_idx) - return (a.vn_idx < b.vn_idx); - if (a.vt_idx != b.vt_idx) - return (a.vt_idx < b.vt_idx); - - return false; -} - -struct obj_shape { - std::vector v; - std::vector vn; - std::vector vt; -}; - -static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } - -static inline bool isNewLine(const char c) { - return (c == '\r') || (c == '\n') || (c == '\0'); -} - -// Make index zero-base, and also support relative index. -static inline int fixIndex(int idx, int n) { - if (idx > 0) return idx - 1; - if (idx == 0) return 0; - return n + idx; // negative value = relative -} - -static inline std::string parseString(const char *&token) { - std::string s; - token += strspn(token, " \t"); - size_t e = strcspn(token, " \t\r"); - s = std::string(token, &token[e]); - token += e; - return s; -} - -static inline int parseInt(const char *&token) { - token += strspn(token, " \t"); - int i = atoi(token); - token += strcspn(token, " \t\r"); - return i; -} - - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) -{ - if (s >= s_end) - { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') - { - sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - goto fail; - } - - // Read the integer part. - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; read++; - } - - // We must make sure we actually got something. - if (read == 0) - goto fail; - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) - goto assemble; - - // Read the decimal part. - if (*curr == '.') - { - curr++; - read = 1; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); - read++; curr++; - } - } - else if (*curr == 'e' || *curr == 'E') {} - else - { - goto assemble; - } - - if (!end_not_reached) - goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') - { - curr++; - // Figure out if a sign is present and if it is. - if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) - { - exp_sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - // Empty E is not allowed. - goto fail; - } - - read = 0; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; read++; - } - exponent *= (exp_sign == '+'? 1 : -1); - if (read == 0) - goto fail; - } - -assemble: - *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); - return true; -fail: - return false; -} -static inline float parseFloat(const char *&token) { - token += strspn(token, " \t"); -#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER - float f = (float)atof(token); - token += strcspn(token, " \t\r"); -#else - const char *end = token + strcspn(token, " \t\r"); - double val = 0.0; - tryParseDouble(token, end, &val); - float f = static_cast(val); - token = end; -#endif - return f; -} - - -static inline void parseFloat2(float &x, float &y, const char *&token) { - x = parseFloat(token); - y = parseFloat(token); -} - -static inline void parseFloat3(float &x, float &y, float &z, - const char *&token) { - x = parseFloat(token); - y = parseFloat(token); - z = parseFloat(token); -} - -// Parse triples: i, i/j/k, i//k, i/j -static vertex_index parseTriple(const char *&token, int vsize, int vnsize, - int vtsize) { - vertex_index vi(-1); - - vi.v_idx = fixIndex(atoi(token), vsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return vi; - } - token++; - - // i//k - if (token[0] == '/') { - token++; - vi.vn_idx = fixIndex(atoi(token), vnsize); - token += strcspn(token, "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = fixIndex(atoi(token), vtsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return vi; - } - - // i/j/k - token++; // skip '/' - vi.vn_idx = fixIndex(atoi(token), vnsize); - token += strcspn(token, "/ \t\r"); - return vi; -} - -static unsigned int -updateVertex(std::map &vertexCache, - std::vector &positions, std::vector &normals, - std::vector &texcoords, - const std::vector &in_positions, - const std::vector &in_normals, - const std::vector &in_texcoords, const vertex_index &i) { - const std::map::iterator it = vertexCache.find(i); - - if (it != vertexCache.end()) { - // found cache - return it->second; - } - - assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2)); - - positions.push_back(in_positions[3 * i.v_idx + 0]); - positions.push_back(in_positions[3 * i.v_idx + 1]); - positions.push_back(in_positions[3 * i.v_idx + 2]); - - if (i.vn_idx >= 0) { - normals.push_back(in_normals[3 * i.vn_idx + 0]); - normals.push_back(in_normals[3 * i.vn_idx + 1]); - normals.push_back(in_normals[3 * i.vn_idx + 2]); - } - - if (i.vt_idx >= 0) { - texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]); - texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]); - } - - unsigned int idx = static_cast(positions.size() / 3 - 1); - vertexCache[i] = idx; - - return idx; -} - -void InitMaterial(material_t &material) { - material.name = ""; - material.ambient_texname = ""; - material.diffuse_texname = ""; - material.specular_texname = ""; - material.specular_highlight_texname = ""; - material.bump_texname = ""; - material.displacement_texname = ""; - material.alpha_texname = ""; - for (int i = 0; i < 3; i++) { - material.ambient[i] = 0.f; - material.diffuse[i] = 0.f; - material.specular[i] = 0.f; - material.transmittance[i] = 0.f; - material.emission[i] = 0.f; - } - material.illum = 0; - material.dissolve = 1.f; - material.shininess = 1.f; - material.ior = 1.f; - material.unknown_parameter.clear(); -} - -static bool exportFaceGroupToShape( - shape_t &shape, std::map vertexCache, - const std::vector &in_positions, - const std::vector &in_normals, - const std::vector &in_texcoords, - const std::vector > &faceGroup, - const int material_id, const std::string &name, bool clearCache) { - if (faceGroup.empty()) { - return false; - } - - // Flatten vertices and indices - for (size_t i = 0; i < faceGroup.size(); i++) { - const std::vector &face = faceGroup[i]; - - vertex_index i0 = face[0]; - vertex_index i1(-1); - vertex_index i2 = face[1]; - - size_t npolys = face.size(); - - // Polygon -> triangle fan conversion - for (size_t k = 2; k < npolys; k++) { - i1 = i2; - i2 = face[k]; - - unsigned int v0 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); - unsigned int v1 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); - unsigned int v2 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); - - shape.mesh.indices.push_back(v0); - shape.mesh.indices.push_back(v1); - shape.mesh.indices.push_back(v2); - - shape.mesh.material_ids.push_back(material_id); - } - } - - shape.name = name; - - if (clearCache) - vertexCache.clear(); - - return true; -} - -void LoadMtl(std::map &material_map, - std::vector &materials, - std::istream &inStream) { - - // Create a default material anyway. - material_t material; - InitMaterial(material); - - int maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. - while (inStream.peek() != -1) { - inStream.getline(&buf[0], maxchars); - - std::string linebuf(&buf[0]); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') - continue; // empty line - - if (token[0] == '#') - continue; // comment line - - // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { - // flush previous material. - if (!material.name.empty()) { - material_map.insert( - std::pair(material.name, static_cast(materials.size()))); - materials.push_back(material); - } - - // initial temporary material - InitMaterial(material); - - // set new mtl name - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - material.name = namebuf; - continue; - } - - // ambient - if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(r, g, b, token); - material.ambient[0] = r; - material.ambient[1] = g; - material.ambient[2] = b; - continue; - } - - // diffuse - if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(r, g, b, token); - material.diffuse[0] = r; - material.diffuse[1] = g; - material.diffuse[2] = b; - continue; - } - - // specular - if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(r, g, b, token); - material.specular[0] = r; - material.specular[1] = g; - material.specular[2] = b; - continue; - } - - // transmittance - if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { - token += 2; - float r, g, b; - parseFloat3(r, g, b, token); - material.transmittance[0] = r; - material.transmittance[1] = g; - material.transmittance[2] = b; - continue; - } - - // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { - token += 2; - material.ior = parseFloat(token); - continue; - } - - // emission - if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { - token += 2; - float r, g, b; - parseFloat3(r, g, b, token); - material.emission[0] = r; - material.emission[1] = g; - material.emission[2] = b; - continue; - } - - // shininess - if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { - token += 2; - material.shininess = parseFloat(token); - continue; - } - - // illum model - if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) { - token += 6; - material.illum = parseInt(token); - continue; - } - - // dissolve - if ((token[0] == 'd' && isSpace(token[1]))) { - token += 1; - material.dissolve = parseFloat(token); - continue; - } - if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { - token += 2; - // Invert value of Tr(assume Tr is in range [0, 1]) - material.dissolve = 1.0f - parseFloat(token); - continue; - } - - // ambient texture - if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { - token += 7; - material.ambient_texname = token; - continue; - } - - // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { - token += 7; - material.diffuse_texname = token; - continue; - } - - // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { - token += 7; - material.specular_texname = token; - continue; - } - - // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { - token += 7; - material.specular_highlight_texname = token; - continue; - } - - // bump texture - if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { - token += 9; - material.bump_texname = token; - continue; - } - - // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { - token += 6; - material.alpha_texname = token; - continue; - } - - // bump texture - if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { - token += 5; - material.bump_texname = token; - continue; - } - - // displacement texture - if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { - token += 5; - material.displacement_texname = token; - continue; - } - - // unknown parameter - const char *_space = strchr(token, ' '); - if (!_space) { - _space = strchr(token, '\t'); - } - if (_space) { - std::ptrdiff_t len = _space - token; - std::string key(token, len); - std::string value = _space + 1; - material.unknown_parameter.insert( - std::pair(key, value)); - } - } - // flush last material. - material_map.insert( - std::pair(material.name, static_cast(materials.size()))); - materials.push_back(material); -} - -bool MaterialFileReader::operator()(const std::string &matId, - std::vector &materials, - std::map &matMap, - std::string& err) { - std::string filepath; - - if (!m_mtlBasePath.empty()) { - filepath = std::string(m_mtlBasePath) + matId; - } else { - filepath = matId; - } - - std::ifstream matIStream(filepath.c_str()); - LoadMtl(matMap, materials, matIStream); - if (!matIStream) { - std::stringstream ss; - ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; - err += ss.str(); - } - return true; -} - -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string &err, - const char *filename, const char *mtl_basepath) { - - shapes.clear(); - - std::stringstream errss; - - std::ifstream ifs(filename); - if (!ifs) { - errss << "Cannot open file [" << filename << "]" << std::endl; - err = errss.str(); - return false; - } - - std::string basePath; - if (mtl_basepath) { - basePath = mtl_basepath; - } - MaterialFileReader matFileReader(basePath); - - return LoadObj(shapes, materials, err, ifs, matFileReader); -} - -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string& err, - std::istream &inStream, MaterialReader &readMatFn) { - std::stringstream errss; - - std::vector v; - std::vector vn; - std::vector vt; - std::vector > faceGroup; - std::string name; - - // material - std::map material_map; - std::map vertexCache; - int material = -1; - - shape_t shape; - - int maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. - while (inStream.peek() != -1) { - inStream.getline(&buf[0], maxchars); - - std::string linebuf(&buf[0]); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') - continue; // empty line - - if (token[0] == '#') - continue; // comment line - - // vertex - if (token[0] == 'v' && isSpace((token[1]))) { - token += 2; - float x, y, z; - parseFloat3(x, y, z, token); - v.push_back(x); - v.push_back(y); - v.push_back(z); - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { - token += 3; - float x, y, z; - parseFloat3(x, y, z, token); - vn.push_back(x); - vn.push_back(y); - vn.push_back(z); - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { - token += 3; - float x, y; - parseFloat2(x, y, token); - vt.push_back(x); - vt.push_back(y); - continue; - } - - // face - if (token[0] == 'f' && isSpace((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - std::vector face; - while (!isNewLine(token[0])) { - vertex_index vi = - parseTriple(token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); - face.push_back(vi); - size_t n = strspn(token, " \t\r"); - token += n; - } - - faceGroup.push_back(face); - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { - - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - - // Create face group per material. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, material, name, true); - if (ret) { - shapes.push_back(shape); - } - shape = shape_t(); - faceGroup.clear(); - - if (material_map.find(namebuf) != material_map.end()) { - material = material_map[namebuf]; - } else { - // { error!! material not found } - material = -1; - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - - std::string err_mtl; - bool ok = readMatFn(namebuf, materials, material_map, err_mtl); - err += err_mtl; - - if (!ok) { - faceGroup.clear(); // for safety - return false; - } - - continue; - } - - // group name - if (token[0] == 'g' && isSpace((token[1]))) { - - // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, material, name, true); - if (ret) { - shapes.push_back(shape); - } - - shape = shape_t(); - - // material = -1; - faceGroup.clear(); - - std::vector names; - while (!isNewLine(token[0])) { - std::string str = parseString(token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - assert(names.size() > 0); - - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; - } else { - name = ""; - } - - continue; - } - - // object name - if (token[0] == 'o' && isSpace((token[1]))) { - - // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, material, name, true); - if (ret) { - shapes.push_back(shape); - } - - // material = -1; - faceGroup.clear(); - shape = shape_t(); - - // @todo { multiple object name? } - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - name = std::string(namebuf); - - continue; - } - - // Ignore unknown command. - } - - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, - material, name, true); - if (ret) { - shapes.push_back(shape); - } - faceGroup.clear(); // for safety - - err += errss.str(); - return true; -} - -} // namespace diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 0a65b3ad..a0834e0f 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -3,6 +3,36 @@ // // Licensed under 2-clause BSD liecense. // + +// +// version 0.9.16: Make tinyobjloader header-only +// version 0.9.15: Change API to handle no mtl file case correctly(#58) +// version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) +// version 0.9.13: Report "Material file not found message" in `err`(#46) +// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) +// version 0.9.11: Invert `Tr` parameter(#43) +// version 0.9.10: Fix seg fault on windows. +// version 0.9.9 : Replace atof() with custom parser. +// version 0.9.8 : Fix multi-materials(per-face material ID). +// version 0.9.7 : Support multi-materials(per-face material ID) per +// object/group. +// version 0.9.6 : Support Ni(index of refraction) mtl parameter. +// Parse transmittance material parameter correctly. +// version 0.9.5 : Parse multiple group name. +// Add support of specifying the base path to load material file. +// version 0.9.4 : Initial suupport of group tag(g) +// version 0.9.3 : Fix parsing triple 'x/y/z' +// version 0.9.2 : Add more .mtl load support +// version 0.9.1 : Add initial .mtl load support +// version 0.9.0 : Initial +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + #ifndef _TINY_OBJ_LOADER_H #define _TINY_OBJ_LOADER_H @@ -100,4 +130,906 @@ void LoadMtl(std::map &material_map, // [output] std::istream &inStream); } +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tiny_obj_loader.h" + +namespace tinyobj { + +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) + +struct vertex_index { + int v_idx, vt_idx, vn_idx; + vertex_index(){}; + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){}; + vertex_index(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){}; +}; +// for std::map +static inline bool operator<(const vertex_index &a, const vertex_index &b) { + if (a.v_idx != b.v_idx) + return (a.v_idx < b.v_idx); + if (a.vn_idx != b.vn_idx) + return (a.vn_idx < b.vn_idx); + if (a.vt_idx != b.vt_idx) + return (a.vt_idx < b.vt_idx); + + return false; +} + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } + +static inline bool isNewLine(const char c) { + return (c == '\r') || (c == '\n') || (c == '\0'); +} + +// Make index zero-base, and also support relative index. +static inline int fixIndex(int idx, int n) { + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative +} + +static inline std::string parseString(const char *&token) { + std::string s; + token += strspn(token, " \t"); + size_t e = strcspn(token, " \t\r"); + s = std::string(token, &token[e]); + token += e; + return s; +} + +static inline int parseInt(const char *&token) { + token += strspn(token, " \t"); + int i = atoi(token); + token += strcspn(token, " \t\r"); + return i; +} + + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) +{ + if (s >= s_end) + { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') + { + sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + goto fail; + } + + // Read the integer part. + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; read++; + } + + // We must make sure we actually got something. + if (read == 0) + goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) + goto assemble; + + // Read the decimal part. + if (*curr == '.') + { + curr++; + read = 1; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); + read++; curr++; + } + } + else if (*curr == 'e' || *curr == 'E') {} + else + { + goto assemble; + } + + if (!end_not_reached) + goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') + { + curr++; + // Figure out if a sign is present and if it is. + if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) + { + exp_sign = *curr; + curr++; + } + else if (isdigit(*curr)) { /* Pass through. */ } + else + { + // Empty E is not allowed. + goto fail; + } + + read = 0; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) + { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; read++; + } + exponent *= (exp_sign == '+'? 1 : -1); + if (read == 0) + goto fail; + } + +assemble: + *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); + return true; +fail: + return false; +} +static inline float parseFloat(const char *&token) { + token += strspn(token, " \t"); +#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER + float f = (float)atof(token); + token += strcspn(token, " \t\r"); +#else + const char *end = token + strcspn(token, " \t\r"); + double val = 0.0; + tryParseDouble(token, end, &val); + float f = static_cast(val); + token = end; +#endif + return f; +} + + +static inline void parseFloat2(float &x, float &y, const char *&token) { + x = parseFloat(token); + y = parseFloat(token); +} + +static inline void parseFloat3(float &x, float &y, float &z, + const char *&token) { + x = parseFloat(token); + y = parseFloat(token); + z = parseFloat(token); +} + +// Parse triples: i, i/j/k, i//k, i/j +static vertex_index parseTriple(const char *&token, int vsize, int vnsize, + int vtsize) { + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi(token), vsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + token++; + + // i//k + if (token[0] == '/') { + token++; + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi(token), vtsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + + // i/j/k + token++; // skip '/' + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; +} + +static unsigned int +updateVertex(std::map &vertexCache, + std::vector &positions, std::vector &normals, + std::vector &texcoords, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, const vertex_index &i) { + const std::map::iterator it = vertexCache.find(i); + + if (it != vertexCache.end()) { + // found cache + return it->second; + } + + assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2)); + + positions.push_back(in_positions[3 * i.v_idx + 0]); + positions.push_back(in_positions[3 * i.v_idx + 1]); + positions.push_back(in_positions[3 * i.v_idx + 2]); + + if (i.vn_idx >= 0) { + normals.push_back(in_normals[3 * i.vn_idx + 0]); + normals.push_back(in_normals[3 * i.vn_idx + 1]); + normals.push_back(in_normals[3 * i.vn_idx + 2]); + } + + if (i.vt_idx >= 0) { + texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]); + texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]); + } + + unsigned int idx = static_cast(positions.size() / 3 - 1); + vertexCache[i] = idx; + + return idx; +} + +void InitMaterial(material_t &material) { + material.name = ""; + material.ambient_texname = ""; + material.diffuse_texname = ""; + material.specular_texname = ""; + material.specular_highlight_texname = ""; + material.bump_texname = ""; + material.displacement_texname = ""; + material.alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material.ambient[i] = 0.f; + material.diffuse[i] = 0.f; + material.specular[i] = 0.f; + material.transmittance[i] = 0.f; + material.emission[i] = 0.f; + } + material.illum = 0; + material.dissolve = 1.f; + material.shininess = 1.f; + material.ior = 1.f; + material.unknown_parameter.clear(); +} + +static bool exportFaceGroupToShape( + shape_t &shape, std::map vertexCache, + const std::vector &in_positions, + const std::vector &in_normals, + const std::vector &in_texcoords, + const std::vector > &faceGroup, + const int material_id, const std::string &name, bool clearCache) { + if (faceGroup.empty()) { + return false; + } + + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const std::vector &face = faceGroup[i]; + + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; + + size_t npolys = face.size(); + + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; + + unsigned int v0 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); + unsigned int v1 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); + unsigned int v2 = updateVertex( + vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); + + shape.mesh.indices.push_back(v0); + shape.mesh.indices.push_back(v1); + shape.mesh.indices.push_back(v2); + + shape.mesh.material_ids.push_back(material_id); + } + } + + shape.name = name; + + if (clearCache) + vertexCache.clear(); + + return true; +} + +void LoadMtl(std::map &material_map, + std::vector &materials, + std::istream &inStream) { + + // Create a default material anyway. + material_t material; + InitMaterial(material); + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map.insert( + std::pair(material.name, static_cast(materials.size()))); + materials.push_back(material); + } + + // initial temporary material + InitMaterial(material); + + // set new mtl name + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { + token += 2; + material.ior = parseFloat(token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { + token += 2; + material.shininess = parseFloat(token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) { + token += 6; + material.illum = parseInt(token); + continue; + } + + // dissolve + if ((token[0] == 'd' && isSpace(token[1]))) { + token += 1; + material.dissolve = parseFloat(token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { + token += 2; + // Invert value of Tr(assume Tr is in range [0, 1]) + material.dissolve = 1.0f - parseFloat(token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { + token += 7; + material.ambient_texname = token; + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { + token += 7; + material.diffuse_texname = token; + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { + token += 7; + material.specular_texname = token; + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { + token += 7; + material.specular_highlight_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { + token += 9; + material.bump_texname = token; + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { + token += 6; + material.alpha_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { + token += 5; + material.bump_texname = token; + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { + token += 5; + material.displacement_texname = token; + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, len); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map.insert( + std::pair(material.name, static_cast(materials.size()))); + materials.push_back(material); +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector &materials, + std::map &matMap, + std::string& err) { + std::string filepath; + + if (!m_mtlBasePath.empty()) { + filepath = std::string(m_mtlBasePath) + matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + LoadMtl(matMap, materials, matIStream); + if (!matIStream) { + std::stringstream ss; + ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; + err += ss.str(); + } + return true; +} + +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string &err, + const char *filename, const char *mtl_basepath) { + + shapes.clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + err = errss.str(); + return false; + } + + std::string basePath; + if (mtl_basepath) { + basePath = mtl_basepath; + } + MaterialFileReader matFileReader(basePath); + + return LoadObj(shapes, materials, err, ifs, matFileReader); +} + +bool LoadObj(std::vector &shapes, // [output] + std::vector &materials, // [output] + std::string& err, + std::istream &inStream, MaterialReader &readMatFn) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector > faceGroup; + std::string name; + + // material + std::map material_map; + std::map vertexCache; + int material = -1; + + shape_t shape; + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') + continue; // empty line + + if (token[0] == '#') + continue; // comment line + + // vertex + if (token[0] == 'v' && isSpace((token[1]))) { + token += 2; + float x, y, z; + parseFloat3(x, y, z, token); + v.push_back(x); + v.push_back(y); + v.push_back(z); + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(x, y, z, token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { + token += 3; + float x, y; + parseFloat2(x, y, token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // face + if (token[0] == 'f' && isSpace((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + std::vector face; + while (!isNewLine(token[0])) { + vertex_index vi = + parseTriple(token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); + face.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { + + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + // Create face group per material. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + shape = shape_t(); + faceGroup.clear(); + + if (material_map.find(namebuf) != material_map.end()) { + material = material_map[namebuf]; + } else { + // { error!! material not found } + material = -1; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + std::string err_mtl; + bool ok = readMatFn(namebuf, materials, material_map, err_mtl); + err += err_mtl; + + if (!ok) { + faceGroup.clear(); // for safety + return false; + } + + continue; + } + + // group name + if (token[0] == 'g' && isSpace((token[1]))) { + + // flush previous face group. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + + shape = shape_t(); + + // material = -1; + faceGroup.clear(); + + std::vector names; + while (!isNewLine(token[0])) { + std::string str = parseString(token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + continue; + } + + // object name + if (token[0] == 'o' && isSpace((token[1]))) { + + // flush previous face group. + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, + faceGroup, material, name, true); + if (ret) { + shapes.push_back(shape); + } + + // material = -1; + faceGroup.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + name = std::string(namebuf); + + continue; + } + + // Ignore unknown command. + } + + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, + material, name, true); + if (ret) { + shapes.push_back(shape); + } + faceGroup.clear(); // for safety + + err += errss.str(); + return true; +} + +} // namespace + + +#endif + #endif // _TINY_OBJ_LOADER_H From 783b8c48869fa5dbafbcf27e5d55f81a67ad5a80 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 5 Dec 2015 12:34:28 +0900 Subject: [PATCH 062/585] Suppress clang compiler warnings. --- test.cc | 2 ++ tiny_obj_loader.h | 46 +++++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/test.cc b/test.cc index 46673eb9..f120c692 100644 --- a/test.cc +++ b/test.cc @@ -164,6 +164,8 @@ std::string matStream( std::map& matMap, std::string& err) { + (void)matId; + (void)err; LoadMtl(matMap, materials, m_matSStream); return true; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index a0834e0f..3f0e7b1c 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -33,8 +33,8 @@ // #include "tiny_obj_loader.h" // -#ifndef _TINY_OBJ_LOADER_H -#define _TINY_OBJ_LOADER_H +#ifndef TINY_OBJ_LOADER_H +#define TINY_OBJ_LOADER_H #include #include @@ -56,6 +56,8 @@ typedef struct { // illumination model (see http://www.fileformat.info/format/material/) int illum; + int dummy; // Supress padding warning. + std::string ambient_texname; // map_Ka std::string diffuse_texname; // map_Kd std::string specular_texname; // map_Ks @@ -82,7 +84,7 @@ typedef struct { class MaterialReader { public: MaterialReader() {} - virtual ~MaterialReader() {} + virtual ~MaterialReader(); virtual bool operator()(const std::string &matId, std::vector &materials, @@ -148,14 +150,16 @@ void LoadMtl(std::map &material_map, // [output] namespace tinyobj { +MaterialReader::~MaterialReader() {} + #define TINYOBJ_SSCANF_BUFFER_SIZE (4096) struct vertex_index { int v_idx, vt_idx, vn_idx; - vertex_index(){}; - vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){}; + vertex_index(){} + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){} vertex_index(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){}; + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){} }; // for std::map static inline bool operator<(const vertex_index &a, const vertex_index &b) { @@ -423,21 +427,21 @@ updateVertex(std::map &vertexCache, return it->second; } - assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2)); + assert(in_positions.size() > static_cast(3 * i.v_idx + 2)); - positions.push_back(in_positions[3 * i.v_idx + 0]); - positions.push_back(in_positions[3 * i.v_idx + 1]); - positions.push_back(in_positions[3 * i.v_idx + 2]); + positions.push_back(in_positions[3 * static_cast(i.v_idx) + 0]); + positions.push_back(in_positions[3 * static_cast(i.v_idx) + 1]); + positions.push_back(in_positions[3 * static_cast(i.v_idx) + 2]); if (i.vn_idx >= 0) { - normals.push_back(in_normals[3 * i.vn_idx + 0]); - normals.push_back(in_normals[3 * i.vn_idx + 1]); - normals.push_back(in_normals[3 * i.vn_idx + 2]); + normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 0]); + normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 1]); + normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 2]); } if (i.vt_idx >= 0) { - texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]); - texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]); + texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 0]); + texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 1]); } unsigned int idx = static_cast(positions.size() / 3 - 1); @@ -446,7 +450,7 @@ updateVertex(std::map &vertexCache, return idx; } -void InitMaterial(material_t &material) { +static void InitMaterial(material_t &material) { material.name = ""; material.ambient_texname = ""; material.diffuse_texname = ""; @@ -529,10 +533,10 @@ void LoadMtl(std::map &material_map, material_t material; InitMaterial(material); - int maxchars = 8192; // Alloc enough size. + size_t maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. while (inStream.peek() != -1) { - inStream.getline(&buf[0], maxchars); + inStream.getline(&buf[0], static_cast(maxchars)); std::string linebuf(&buf[0]); @@ -738,7 +742,7 @@ void LoadMtl(std::map &material_map, } if (_space) { std::ptrdiff_t len = _space - token; - std::string key(token, len); + std::string key(token, static_cast(len)); std::string value = _space + 1; material.unknown_parameter.insert( std::pair(key, value)); @@ -817,7 +821,7 @@ bool LoadObj(std::vector &shapes, // [output] shape_t shape; int maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. + std::vector buf(static_cast(maxchars)); // Alloc enough size. while (inStream.peek() != -1) { inStream.getline(&buf[0], maxchars); @@ -1032,4 +1036,4 @@ bool LoadObj(std::vector &shapes, // [output] #endif -#endif // _TINY_OBJ_LOADER_H +#endif // TINY_OBJ_LOADER_H From 85d18ea7cd375f1767c7b23be26abe8a3a5e31d7 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 5 Dec 2015 12:34:44 +0900 Subject: [PATCH 063/585] Add simple ninja script. --- build.ninja | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 build.ninja diff --git a/build.ninja b/build.ninja new file mode 100644 index 00000000..3a26d974 --- /dev/null +++ b/build.ninja @@ -0,0 +1,16 @@ +# build.ninja +cc = clang +cxx = clang++ +cflags = -Werror -Weverything +cxxflags = -Werror -Weverything + +rule compile + command = $cxx $cxxflags -c $in -o $out + +rule link + command = $cxx $in -o $out + +build test.o: compile test.cc +build test: link test.o + +default test From 320a670bc8f5d2d7ea5064c21caeaa6e77bdf1b2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 4 Jan 2016 23:24:43 +0900 Subject: [PATCH 064/585] Add Travis CI script. --- .travis.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..706b8c1b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: cpp + +compiler: gcc + +before_install: + - curl -L "https://dl.bintray.com/gogoprog/gengine/linux64/premake4" -o premake4 + - chmod +x premake4 + +script: + - ./premake4 gmake + - make + - ./test_tinyobjloader From 74614d9fd9ce1d4728bdb15063939c57b2a59a97 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 4 Jan 2016 23:26:29 +0900 Subject: [PATCH 065/585] Add Travis badge. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 77dc472c..24bd1214 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ tinyobjloader [![Join the chat at https://gitter.im/syoyo/tinyobjloader](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/syoyo/tinyobjloader?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/syoyo/tinyobjloader.svg)](https://travis-ci.org/syoyo/tinyobjloader) + [![wercker status](https://app.wercker.com/status/495a3bac400212cdacdeb4dd9397bf4f/m "wercker status")](https://app.wercker.com/project/bykey/495a3bac400212cdacdeb4dd9397bf4f) [![Build status](https://ci.appveyor.com/api/projects/status/tlb421q3t2oyobcn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader/branch/master) From 0e23d499c7db3d77d4bf858d7f4a0d0275e9f064 Mon Sep 17 00:00:00 2001 From: mogemimi Date: Mon, 18 Jan 2016 08:05:54 +0900 Subject: [PATCH 066/585] Fix minor typos --- tiny_obj_loader.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 3f0e7b1c..021e4200 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1,7 +1,7 @@ // // Copyright 2012-2015, Syoyo Fujita. // -// Licensed under 2-clause BSD liecense. +// Licensed under 2-clause BSD license. // // @@ -20,7 +20,7 @@ // Parse transmittance material parameter correctly. // version 0.9.5 : Parse multiple group name. // Add support of specifying the base path to load material file. -// version 0.9.4 : Initial suupport of group tag(g) +// version 0.9.4 : Initial support of group tag(g) // version 0.9.3 : Fix parsing triple 'x/y/z' // version 0.9.2 : Add more .mtl load support // version 0.9.1 : Add initial .mtl load support @@ -56,7 +56,7 @@ typedef struct { // illumination model (see http://www.fileformat.info/format/material/) int illum; - int dummy; // Supress padding warning. + int dummy; // Suppress padding warning. std::string ambient_texname; // map_Ka std::string diffuse_texname; // map_Kd From 2fcdd32bc37c28c9219aa2e8843a7082aa505e2f Mon Sep 17 00:00:00 2001 From: dboogert Date: Thu, 28 Jan 2016 18:34:08 +0000 Subject: [PATCH 067/585] subd support: n-sided polygons & pixar crease tags + optional parameter to triangulate polygons to maintain compatibility + added create tag test & example subd crease tag file from OpenSubD source code --- catmark_torus_creases0.obj | 101 ++++++++++++++++++++++ test.cc | 77 +++++++++++++---- tiny_obj_loader.cc | 169 ++++++++++++++++++++++++++++--------- tiny_obj_loader.h | 18 +++- 4 files changed, 309 insertions(+), 56 deletions(-) create mode 100644 catmark_torus_creases0.obj diff --git a/catmark_torus_creases0.obj b/catmark_torus_creases0.obj new file mode 100644 index 00000000..bf18f158 --- /dev/null +++ b/catmark_torus_creases0.obj @@ -0,0 +1,101 @@ +# +# Copyright 2013 Pixar +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# +# This file uses centimeters as units for non-parametric coordinates. + +v 1.25052 0.517982 0.353553 +v 0.597239 0.247384 0.353553 +v 0.597239 0.247384 -0.353553 +v 1.25052 0.517982 -0.353553 +v 0.517982 1.25052 0.353553 +v 0.247384 0.597239 0.353553 +v 0.247384 0.597239 -0.353553 +v 0.517982 1.25052 -0.353553 +v -0.517982 1.25052 0.353553 +v -0.247384 0.597239 0.353553 +v -0.247384 0.597239 -0.353553 +v -0.517982 1.25052 -0.353553 +v -1.25052 0.517982 0.353553 +v -0.597239 0.247384 0.353553 +v -0.597239 0.247384 -0.353553 +v -1.25052 0.517982 -0.353553 +v -1.25052 -0.517982 0.353553 +v -0.597239 -0.247384 0.353553 +v -0.597239 -0.247384 -0.353553 +v -1.25052 -0.517982 -0.353553 +v -0.517982 -1.25052 0.353553 +v -0.247384 -0.597239 0.353553 +v -0.247384 -0.597239 -0.353553 +v -0.517982 -1.25052 -0.353553 +v 0.517982 -1.25052 0.353553 +v 0.247384 -0.597239 0.353553 +v 0.247384 -0.597239 -0.353553 +v 0.517982 -1.25052 -0.353553 +v 1.25052 -0.517982 0.353553 +v 0.597239 -0.247384 0.353553 +v 0.597239 -0.247384 -0.353553 +v 1.25052 -0.517982 -0.353553 +vt 0 0 +vt 1 0 +vt 1 1 +vt 0 1 +f 5/1/1 6/2/2 2/3/3 1/4/4 +f 6/1/5 7/2/6 3/3/7 2/4/8 +f 7/1/9 8/2/10 4/3/11 3/4/12 +f 8/1/13 5/2/14 1/3/15 4/4/16 +f 9/1/17 10/2/18 6/3/19 5/4/20 +f 10/1/21 11/2/22 7/3/23 6/4/24 +f 11/1/25 12/2/26 8/3/27 7/4/28 +f 12/1/29 9/2/30 5/3/31 8/4/32 +f 13/1/33 14/2/34 10/3/35 9/4/36 +f 14/1/37 15/2/38 11/3/39 10/4/40 +f 15/1/41 16/2/42 12/3/43 11/4/44 +f 16/1/45 13/2/46 9/3/47 12/4/48 +f 17/1/49 18/2/50 14/3/51 13/4/52 +f 18/1/53 19/2/54 15/3/55 14/4/56 +f 19/1/57 20/2/58 16/3/59 15/4/60 +f 20/1/61 17/2/62 13/3/63 16/4/64 +f 21/1/65 22/2/66 18/3/67 17/4/68 +f 22/1/69 23/2/70 19/3/71 18/4/72 +f 23/1/73 24/2/74 20/3/75 19/4/76 +f 24/1/77 21/2/78 17/3/79 20/4/80 +f 25/1/81 26/2/82 22/3/83 21/4/84 +f 26/1/85 27/2/86 23/3/87 22/4/88 +f 27/1/89 28/2/90 24/3/91 23/4/92 +f 28/1/93 25/2/94 21/3/95 24/4/96 +f 29/1/97 30/2/98 26/3/99 25/4/100 +f 30/1/101 31/2/102 27/3/103 26/4/104 +f 31/1/105 32/2/106 28/3/107 27/4/108 +f 32/1/109 29/2/110 25/3/111 28/4/112 +f 1/1/113 2/2/114 30/3/115 29/4/116 +f 2/1/117 3/2/118 31/3/119 30/4/120 +f 3/1/121 4/2/122 32/3/123 31/4/124 +f 4/1/125 1/2/126 29/3/127 32/4/128 +t crease 2/1/0 1 5 4.7 +t crease 2/1/0 5 9 4.7 +t crease 2/1/0 9 13 4.7 +t crease 2/1/0 13 17 4.7 +t crease 2/1/0 17 21 4.7 +t crease 2/1/0 21 25 4.7 +t crease 2/1/0 25 29 4.7 +t crease 2/1/0 29 1 4.7 diff --git a/test.cc b/test.cc index 82b6178c..02f47029 100644 --- a/test.cc +++ b/test.cc @@ -7,14 +7,16 @@ #include #include -static void PrintInfo(const std::vector& shapes) +static void PrintInfo(const std::vector& shapes, bool triangulate = true) { - std::cout << "# of shapes : " << shapes.size() << std::endl; for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); printf("shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); + if (triangulate) + { + assert((shapes[i].mesh.indices.size() % 3) == 0); + } for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { printf(" idx[%ld] = %d\n", f, shapes[i].mesh.indices[f]); } @@ -27,7 +29,51 @@ static void PrintInfo(const std::vector& shapes) shapes[i].mesh.positions[3*v+1], shapes[i].mesh.positions[3*v+2]); } - + + printf("shape[%ld].numFaces: %ld\n", i, shapes[i].mesh.numVertices.size()); + for (size_t v = 0; v < shapes[i].mesh.numVertices.size(); v++) { + printf(" numVerts[%ld] = %ld\n", v, + (long) shapes[i].mesh.numVertices[v]); + } + + printf("shape[%ld].numTags: %ld\n", i, shapes[i].mesh.tags.size()); + for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { + printf(" tag[%ld] = %s ", t, shapes[i].mesh.tags[t].name.c_str()); + printf(" ints: ["); + for (int j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) + { + printf("%ld", (long) shapes[i].mesh.tags[t].intValues[j]); + if (j < (shapes[i].mesh.tags[t].intValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + + printf(" floats: ["); + for (int j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) + { + printf("%f", shapes[i].mesh.tags[t].floatValues[j]); + if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + + printf(" strings: ["); + for (int j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) + { + printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); + if (j < (shapes[i].mesh.tags[t].stringValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + printf("\n"); + } + printf("shape[%ld].material.name = %s\n", i, shapes[i].material.name.c_str()); printf(" material.Ka = (%f, %f ,%f)\n", shapes[i].material.ambient[0], shapes[i].material.ambient[1], shapes[i].material.ambient[2]); printf(" material.Kd = (%f, %f ,%f)\n", shapes[i].material.diffuse[0], shapes[i].material.diffuse[1], shapes[i].material.diffuse[2]); @@ -54,19 +100,20 @@ static void PrintInfo(const std::vector& shapes) static bool TestLoadObj( const char* filename, - const char* basepath = NULL) + const char* basepath = NULL, + bool triangulate = true) { std::cout << "Loading " << filename << std::endl; std::vector shapes; - std::string err = tinyobj::LoadObj(shapes, filename, basepath); + std::string err = tinyobj::LoadObj(shapes, filename, basepath, triangulate); if (!err.empty()) { std::cerr << err << std::endl; return false; } - PrintInfo(shapes); + PrintInfo(shapes, triangulate); return true; } @@ -78,7 +125,7 @@ TestStreamLoadObj() std::cout << "Stream Loading " << std::endl; std::stringstream objStream; - objStream + objStream << "mtllib cube.mtl\n" "\n" "v 0.000000 2.000000 2.000000\n" @@ -111,7 +158,7 @@ TestStreamLoadObj() "f 2 6 7 3\n" "# 6 elements"; -std::string matStream( +std::string matStream( "newmtl white\n" "Ka 0 0 0\n" "Kd 1 1 1\n" @@ -153,19 +200,19 @@ std::string matStream( private: std::stringstream m_matSStream; - }; + }; MaterialStringStreamReader matSSReader(matStream); std::vector shapes; - std::string err = tinyobj::LoadObj(shapes, objStream, matSSReader); - + std::string err = tinyobj::LoadObj(shapes, objStream, matSSReader); + if (!err.empty()) { std::cerr << err << std::endl; return false; } PrintInfo(shapes); - + return true; } @@ -174,7 +221,6 @@ main( int argc, char **argv) { - if (argc > 1) { const char* basepath = NULL; if (argc > 2) { @@ -185,7 +231,8 @@ main( assert(true == TestLoadObj("cornell_box.obj")); assert(true == TestLoadObj("cube.obj")); assert(true == TestStreamLoadObj()); + assert(true == TestLoadObj("catmark_torus_creases0.obj", NULL, false)); } - + return 0; } diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 50fa69bd..1f65ffd4 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -1,6 +1,6 @@ // // Copyright 2012-2013, Syoyo Fujita. -// +// // Licensed under 2-clause BSD liecense. // @@ -38,6 +38,14 @@ struct vertex_index { vertex_index(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}; }; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {} + int num_ints; + int num_floats; + int num_strings; +}; + // for std::map static inline bool operator<(const vertex_index& a, const vertex_index& b) { @@ -62,7 +70,7 @@ static inline bool isNewLine(const char c) { return (c == '\r') || (c == '\n') || (c == '\0'); } -// Make index zero-base, and also support relative index. +// Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { int i; @@ -122,6 +130,30 @@ static inline void parseFloat3( } +static tag_sizes parseTagTriple(const char* & token) +{ + tag_sizes ts; + + ts.num_ints = atoi(token); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return ts; + } + token++; + + ts.num_floats = atoi(token); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return ts; + } + token++; + + ts.num_strings = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + + return ts; +} + // Parse triples: i, i/j/k, i//k, i/j static vertex_index parseTriple( const char* &token, @@ -145,7 +177,7 @@ static vertex_index parseTriple( token += strcspn(token, "/ \t\r"); return vi; } - + // i/j/k or i/j vi.vt_idx = fixIndex(atoi(token), vtsize); token += strcspn(token, "/ \t\r"); @@ -157,7 +189,7 @@ static vertex_index parseTriple( token++; // skip '/' vi.vn_idx = fixIndex(atoi(token), vnsize); token += strcspn(token, "/ \t\r"); - return vi; + return vi; } static unsigned int @@ -184,13 +216,13 @@ updateVertex( positions.push_back(in_positions[3*i.v_idx+1]); positions.push_back(in_positions[3*i.v_idx+2]); - if (i.vn_idx >= 0) { + if (i.vn_idx >= 0 && (i.vn_idx * 3 + 2) < in_normals.size() ) { normals.push_back(in_normals[3*i.vn_idx+0]); normals.push_back(in_normals[3*i.vn_idx+1]); normals.push_back(in_normals[3*i.vn_idx+2]); } - if (i.vt_idx >= 0) { + if (i.vt_idx >= 0 && (i.vt_idx * 2 +1) < in_texcoords.size()) { texcoords.push_back(in_texcoords[2*i.vt_idx+0]); texcoords.push_back(in_texcoords[2*i.vt_idx+1]); } @@ -228,9 +260,11 @@ exportFaceGroupToShape( const std::vector &in_normals, const std::vector &in_texcoords, const std::vector >& faceGroup, + std::vector& tags, const material_t &material, const std::string &name, - const bool is_material_seted) + const bool is_material_seted, + const bool triangulate) { if (faceGroup.empty()) { return false; @@ -247,26 +281,38 @@ exportFaceGroupToShape( for (size_t i = 0; i < faceGroup.size(); i++) { const std::vector& face = faceGroup[i]; - vertex_index i0 = face[0]; - vertex_index i1(-1); - vertex_index i2 = face[1]; - size_t npolys = face.size(); - // Polygon -> triangle fan conversion - for (size_t k = 2; k < npolys; k++) { - i1 = i2; - i2 = face[k]; + if (triangulate) { + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; - unsigned int v0 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i0); - unsigned int v1 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i1); - unsigned int v2 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i2); + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; - indices.push_back(v0); - indices.push_back(v1); - indices.push_back(v2); + unsigned int v0 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i0); + unsigned int v1 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i1); + unsigned int v2 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i2); + + indices.push_back(v0); + indices.push_back(v1); + indices.push_back(v2); + + shape.mesh.numVertices.push_back(3); + } } + else + { + for (size_t k = 0; k < npolys; k++) { + unsigned int v = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, face[k]); + indices.push_back(v); + shape.mesh.numVertices.push_back(npolys); + } + } } // @@ -277,6 +323,7 @@ exportFaceGroupToShape( shape.mesh.normals.swap(normals); shape.mesh.texcoords.swap(texcoords); shape.mesh.indices.swap(indices); + shape.mesh.tags.swap(tags); if(is_material_seted) { shape.material = material; @@ -299,7 +346,7 @@ std::string LoadMtl ( std::stringstream err; material_t material; - + int maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. while (inStream.peek() != -1) { @@ -326,9 +373,9 @@ std::string LoadMtl ( assert(token); if (token[0] == '\0') continue; // empty line - + if (token[0] == '#') continue; // comment line - + // new mtl if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { // flush previous material. @@ -344,7 +391,7 @@ std::string LoadMtl ( material.name = namebuf; continue; } - + // ambient if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { token += 2; @@ -355,7 +402,7 @@ std::string LoadMtl ( material.ambient[2] = b; continue; } - + // diffuse if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { token += 2; @@ -366,7 +413,7 @@ std::string LoadMtl ( material.diffuse[2] = b; continue; } - + // specular if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { token += 2; @@ -377,7 +424,7 @@ std::string LoadMtl ( material.specular[2] = b; continue; } - + // transmittance if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { token += 2; @@ -499,7 +546,8 @@ std::string LoadObj( std::vector& shapes, const char* filename, - const char* mtl_basepath) + const char* mtl_basepath, + bool triangulate) { shapes.clear(); @@ -517,20 +565,23 @@ LoadObj( basePath = mtl_basepath; } MaterialFileReader matFileReader( basePath ); - - return LoadObj(shapes, ifs, matFileReader); + + return LoadObj(shapes, ifs, matFileReader, triangulate); } std::string LoadObj( std::vector& shapes, std::istream& inStream, - MaterialReader& readMatFn) + MaterialReader& readMatFn, + bool triangulate) { std::stringstream err; std::vector v; std::vector vn; std::vector vt; + std::vector tags; + std::vector > faceGroup; std::string name; @@ -565,7 +616,7 @@ std::string LoadObj( assert(token); if (token[0] == '\0') continue; // empty line - + if (token[0] == '#') continue; // comment line // vertex @@ -614,7 +665,7 @@ std::string LoadObj( } faceGroup.push_back(face); - + continue; } @@ -641,13 +692,13 @@ std::string LoadObj( char namebuf[4096]; token += 7; sscanf(token, "%s", namebuf); - + std::string err_mtl = readMatFn(namebuf, material_map); if (!err_mtl.empty()) { faceGroup.clear(); // for safety return err_mtl; } - + continue; } @@ -656,7 +707,7 @@ std::string LoadObj( // flush previous face group. shape_t shape; - bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name, is_material_seted); + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, material, name, is_material_seted, triangulate); if (ret) { shapes.push_back(shape); } @@ -688,7 +739,7 @@ std::string LoadObj( // flush previous face group. shape_t shape; - bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name, is_material_seted); + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, material, name, is_material_seted, triangulate); if (ret) { shapes.push_back(shape); } @@ -706,11 +757,51 @@ std::string LoadObj( continue; } + if (token[0] == 't' && isSpace(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; + sscanf(token, "%s", namebuf); + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(token); + + tag.intValues.resize(ts.num_ints); + + for(int i = 0; i < ts.num_ints; ++i) + { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(ts.num_floats); + for(int i = 0; i < ts.num_floats; ++i) + { + tag.floatValues[i] = parseFloat(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(ts.num_strings); + for(int i = 0; i < ts.num_strings; ++i) + { + char stringValueBuffer[4096]; + + sscanf(token, "%s", stringValueBuffer); + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } + // Ignore unknown command. } shape_t shape; - bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name, is_material_seted); + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, material, name, is_material_seted, triangulate); if (ret) { shapes.push_back(shape); } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index c275a881..bcafbf85 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -34,12 +34,23 @@ typedef struct std::map unknown_parameter; } material_t; +typedef struct +{ + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +} tag_t; + typedef struct { std::vector positions; std::vector normals; std::vector texcoords; std::vector indices; + std::vector numVertices; //Is it worth it 255 faces + std::vector tags; } mesh_t; typedef struct @@ -82,7 +93,9 @@ class MaterialFileReader: std::string LoadObj( std::vector& shapes, // [output] const char* filename, - const char* mtl_basepath = NULL); + const char* mtl_basepath = NULL, + bool triangulate = true +); /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. @@ -90,7 +103,8 @@ std::string LoadObj( std::string LoadObj( std::vector& shapes, // [output] std::istream& inStream, - MaterialReader& readMatFn); + MaterialReader& readMatFn, + bool triangulate = true); /// Loads materials into std::map /// Returns an empty string if successful From 968ad8248e0ae2e5cf232e9a81e4ec4f0d124f97 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 29 Jan 2016 19:55:04 +0900 Subject: [PATCH 068/585] Cosmetic updates. --- README.md | 6 ++++-- tiny_obj_loader.h | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1c0fc229..35cf1006 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Tiny but poweful single file wavefront obj loader written in C++. No dependency What's new ---------- -* Jan 29, 2016 : Support n-polygon and OpenSubdiv crease tag! Thanks dboogert! +* Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert! * Nov 26, 2015 : Now single-header only!. * Nov 08, 2015 : Improved API. * Jun 23, 2015 : Various fixes and added more projects using tinyobjloader. Thanks many contributors! @@ -84,6 +84,8 @@ Licensed under 2 clause BSD. Usage ----- +TinyObjLoader triangulate input .obj by default. + #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc #include "tiny_obj_loader.h" @@ -175,7 +177,7 @@ Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces( for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { int ngon = shapes[i].mesh.num_vertices[n]; for (size_t f = 0; f < ngon; f++) { - size_t v = shapes[i].mesh.indices[indexOffset + f]; + unsigend int v = shapes[i].mesh.indices[indexOffset + f]; printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, shapes[i].mesh.positions[3*v+0], shapes[i].mesh.positions[3*v+1], diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 1ad3f5cc..9665d42b 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -84,8 +84,8 @@ typedef struct std::vector normals; std::vector texcoords; std::vector indices; - std::vector num_vertices; // up to 255 faces - std::vector material_ids; // per-mesh material ID + std::vector num_vertices; // The number of vertices per face. Up to 255. + std::vector material_ids; // per-face material ID std::vector tags; // SubD tag } mesh_t; From 41515c8c7815e315dc81460930dc9c6467924243 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 30 Jan 2016 00:48:42 +0900 Subject: [PATCH 069/585] Apply clang-format. --- .clang-format | 7 + tiny_obj_loader.h | 470 +++++++++++++++++++++++----------------------- 2 files changed, 238 insertions(+), 239 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..a94a2c44 --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +BreakBeforeBraces: Attach +Standard: Cpp03 diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 9665d42b..4240c66a 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -8,9 +8,11 @@ // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) // version 0.9.16: Make tinyobjloader header-only // version 0.9.15: Change API to handle no mtl file case correctly(#58) -// version 0.9.14: Support specular highlight, bump, displacement and alpha map(#53) +// version 0.9.14: Support specular highlight, bump, displacement and alpha +// map(#53) // version 0.9.13: Report "Material file not found message" in `err`(#46) -// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before 'g' (#44) +// version 0.9.12: Fix groups being ignored if they have 'usemtl' just before +// 'g' (#44) // version 0.9.11: Invert `Tr` parameter(#43) // version 0.9.10: Fix seg fault on windows. // version 0.9.9 : Replace atof() with custom parser. @@ -20,7 +22,8 @@ // version 0.9.6 : Support Ni(index of refraction) mtl parameter. // Parse transmittance material parameter correctly. // version 0.9.5 : Parse multiple group name. -// Add support of specifying the base path to load material file. +// Add support of specifying the base path to load material +// file. // version 0.9.4 : Initial support of group tag(g) // version 0.9.3 : Fix parsing triple 'x/y/z' // version 0.9.2 : Add more .mtl load support @@ -32,7 +35,7 @@ // Use this in *one* .cc // #define TINYOBJLOADER_IMPLEMENTATION // #include "tiny_obj_loader.h" -// +// #ifndef TINY_OBJ_LOADER_H #define TINY_OBJ_LOADER_H @@ -69,24 +72,23 @@ typedef struct { std::map unknown_parameter; } material_t; -typedef struct -{ - std::string name; +typedef struct { + std::string name; - std::vector intValues; - std::vector floatValues; - std::vector stringValues; + std::vector intValues; + std::vector floatValues; + std::vector stringValues; } tag_t; -typedef struct -{ - std::vector positions; - std::vector normals; - std::vector texcoords; - std::vector indices; - std::vector num_vertices; // The number of vertices per face. Up to 255. - std::vector material_ids; // per-face material ID - std::vector tags; // SubD tag +typedef struct { + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; + std::vector + num_vertices; // The number of vertices per face. Up to 255. + std::vector material_ids; // per-face material ID + std::vector tags; // SubD tag } mesh_t; typedef struct { @@ -111,9 +113,8 @@ class MaterialFileReader : public MaterialReader { : m_mtlBasePath(mtl_basepath) {} virtual ~MaterialFileReader() {} virtual bool operator()(const std::string &matId, - std::vector &materials, - std::map &matMap, - std::string &err); + std::vector &materials, + std::map &matMap, std::string &err); private: std::string m_mtlBasePath; @@ -125,11 +126,13 @@ class MaterialFileReader : public MaterialReader { /// Returns true when loading .obj become success. /// Returns warning and error message into `err` /// 'mtl_basepath' is optional, and used for base path for .mtl file. -/// 'triangulate' is optional, and used whether triangulate polygon face in .obj or not. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. bool LoadObj(std::vector &shapes, // [output] std::vector &materials, // [output] - std::string& err, // [output] - const char *filename, const char *mtl_basepath = NULL, bool triangulate = true); + std::string &err, // [output] + const char *filename, const char *mtl_basepath = NULL, + bool triangulate = true); /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. @@ -137,8 +140,9 @@ bool LoadObj(std::vector &shapes, // [output] /// Returns warning and error message into `err` bool LoadObj(std::vector &shapes, // [output] std::vector &materials, // [output] - std::string& err, // [output] - std::istream &inStream, MaterialReader &readMatFn, bool triangulate = true); + std::string &err, // [output] + std::istream &inStream, MaterialReader &readMatFn, + bool triangulate = true); /// Loads materials into std::map void LoadMtl(std::map &material_map, // [output] @@ -166,21 +170,21 @@ namespace tinyobj { MaterialReader::~MaterialReader() {} -#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) struct vertex_index { int v_idx, vt_idx, vn_idx; - vertex_index(){} - vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){} + vertex_index() {} + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} vertex_index(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){} + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} }; struct tag_sizes { - tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {} - int num_ints; - int num_floats; - int num_strings; + tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {} + int num_ints; + int num_floats; + int num_strings; }; // for std::map @@ -209,8 +213,10 @@ static inline bool isNewLine(const char c) { // Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { - if (idx > 0) return idx - 1; - if (idx == 0) return 0; + if (idx > 0) + return idx - 1; + if (idx == 0) + return 0; return n + idx; // negative value = relative } @@ -230,7 +236,6 @@ static inline int parseInt(const char *&token) { return i; } - // Tries to parse a floating point number located at s. // // s_end should be a location in the string where reading should absolutely @@ -247,7 +252,7 @@ static inline int parseInt(const char *&token) { // Valid strings are for example: // -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 // -// If the parsing is a success, result is set to the parsed value and true +// If the parsing is a success, result is set to the parsed value and true // is returned. // // The function is greedy and will parse until any of the following happens: @@ -257,121 +262,110 @@ static inline int parseInt(const char *&token) { // The following situations triggers a failure: // - s >= s_end. // - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) -{ - if (s >= s_end) - { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') - { - sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - goto fail; - } - - // Read the integer part. - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; read++; - } - - // We must make sure we actually got something. - if (read == 0) - goto fail; - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) - goto assemble; - - // Read the decimal part. - if (*curr == '.') - { - curr++; - read = 1; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); - read++; curr++; - } - } - else if (*curr == 'e' || *curr == 'E') {} - else - { - goto assemble; - } - - if (!end_not_reached) - goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') - { - curr++; - // Figure out if a sign is present and if it is. - if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) - { - exp_sign = *curr; - curr++; - } - else if (isdigit(*curr)) { /* Pass through. */ } - else - { - // Empty E is not allowed. - goto fail; - } - - read = 0; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) - { - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; read++; - } - exponent *= (exp_sign == '+'? 1 : -1); - if (read == 0) - goto fail; - } +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } else if (isdigit(*curr)) { /* Pass through. */ + } else { + goto fail; + } + + // Read the integer part. + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + } + + // We must make sure we actually got something. + if (read == 0) + goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) + goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); + read++; + curr++; + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) + goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (isdigit(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) + goto fail; + } assemble: - *result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); - return true; + *result = + (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent); + return true; fail: - return false; + return false; } static inline float parseFloat(const char *&token) { token += strspn(token, " \t"); @@ -388,7 +382,6 @@ static inline float parseFloat(const char *&token) { return f; } - static inline void parseFloat2(float &x, float &y, const char *&token) { x = parseFloat(token); y = parseFloat(token); @@ -401,28 +394,27 @@ static inline void parseFloat3(float &x, float &y, float &z, z = parseFloat(token); } -static tag_sizes parseTagTriple(const char* & token) -{ - tag_sizes ts; +static tag_sizes parseTagTriple(const char *&token) { + tag_sizes ts; - ts.num_ints = atoi(token); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return ts; - } - token++; + ts.num_ints = atoi(token); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return ts; + } + token++; - ts.num_floats = atoi(token); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { - return ts; - } - token++; + ts.num_floats = atoi(token); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return ts; + } + token++; - ts.num_strings = atoi(token); - token += strcspn(token, "/ \t\r") + 1; + ts.num_strings = atoi(token); + token += strcspn(token, "/ \t\r") + 1; - return ts; + return ts; } // Parse triples: i, i/j/k, i//k, i/j @@ -479,13 +471,15 @@ updateVertex(std::map &vertexCache, positions.push_back(in_positions[3 * static_cast(i.v_idx) + 1]); positions.push_back(in_positions[3 * static_cast(i.v_idx) + 2]); - if ((i.vn_idx >= 0) && (static_cast(i.vn_idx * 3 + 2) < in_normals.size())) { + if ((i.vn_idx >= 0) && + (static_cast(i.vn_idx * 3 + 2) < in_normals.size())) { normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 0]); normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 1]); normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 2]); } - if ((i.vt_idx >= 0) && (static_cast(i.vt_idx * 2 + 1) < in_texcoords.size())) { + if ((i.vt_idx >= 0) && + (static_cast(i.vt_idx * 2 + 1) < in_texcoords.size())) { texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 0]); texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 1]); } @@ -525,8 +519,8 @@ static bool exportFaceGroupToShape( const std::vector &in_normals, const std::vector &in_texcoords, const std::vector > &faceGroup, - std::vector &tags, - const int material_id, const std::string &name, bool clearCache, bool triangulate) { + std::vector &tags, const int material_id, const std::string &name, + bool clearCache, bool triangulate) { if (faceGroup.empty()) { return false; } @@ -564,22 +558,20 @@ static bool exportFaceGroupToShape( shape.mesh.num_vertices.push_back(3); shape.mesh.material_ids.push_back(material_id); - } } else { for (size_t k = 0; k < npolys; k++) { - unsigned int v = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, face[k]); - + unsigned int v = + updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, + shape.mesh.texcoords, in_positions, in_normals, + in_texcoords, face[k]); + shape.mesh.indices.push_back(v); - } - shape.mesh.num_vertices.push_back(static_cast(npolys)); + shape.mesh.num_vertices.push_back(static_cast(npolys)); shape.mesh.material_ids.push_back(material_id); // per face - } } @@ -593,14 +585,13 @@ static bool exportFaceGroupToShape( } void LoadMtl(std::map &material_map, - std::vector &materials, - std::istream &inStream) { + std::vector &materials, std::istream &inStream) { // Create a default material anyway. material_t material; InitMaterial(material); - size_t maxchars = 8192; // Alloc enough size. + size_t maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. while (inStream.peek() != -1) { inStream.getline(&buf[0], static_cast(maxchars)); @@ -637,8 +628,8 @@ void LoadMtl(std::map &material_map, if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { // flush previous material. if (!material.name.empty()) { - material_map.insert( - std::pair(material.name, static_cast(materials.size()))); + material_map.insert(std::pair( + material.name, static_cast(materials.size()))); materials.push_back(material); } @@ -816,15 +807,15 @@ void LoadMtl(std::map &material_map, } } // flush last material. - material_map.insert( - std::pair(material.name, static_cast(materials.size()))); + material_map.insert(std::pair( + material.name, static_cast(materials.size()))); materials.push_back(material); } bool MaterialFileReader::operator()(const std::string &matId, std::vector &materials, std::map &matMap, - std::string& err) { + std::string &err) { std::string filepath; if (!m_mtlBasePath.empty()) { @@ -837,16 +828,17 @@ bool MaterialFileReader::operator()(const std::string &matId, LoadMtl(matMap, materials, matIStream); if (!matIStream) { std::stringstream ss; - ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; + ss << "WARN: Material file [ " << filepath + << " ] not found. Created a default material."; err += ss.str(); } return true; } -bool LoadObj(std::vector &shapes, // [output] +bool LoadObj(std::vector &shapes, // [output] std::vector &materials, // [output] - std::string &err, - const char *filename, const char *mtl_basepath, bool trianglulate) { + std::string &err, const char *filename, const char *mtl_basepath, + bool trianglulate) { shapes.clear(); @@ -868,10 +860,10 @@ bool LoadObj(std::vector &shapes, // [output] return LoadObj(shapes, materials, err, ifs, matFileReader, trianglulate); } -bool LoadObj(std::vector &shapes, // [output] +bool LoadObj(std::vector &shapes, // [output] std::vector &materials, // [output] - std::string& err, - std::istream &inStream, MaterialReader &readMatFn, bool triangulate) { + std::string &err, std::istream &inStream, + MaterialReader &readMatFn, bool triangulate) { std::stringstream errss; std::vector v; @@ -888,7 +880,7 @@ bool LoadObj(std::vector &shapes, // [output] shape_t shape; - int maxchars = 8192; // Alloc enough size. + int maxchars = 8192; // Alloc enough size. std::vector buf(static_cast(maxchars)); // Alloc enough size. while (inStream.peek() != -1) { inStream.getline(&buf[0], maxchars); @@ -960,8 +952,9 @@ bool LoadObj(std::vector &shapes, // [output] std::vector face; while (!isNewLine(token[0])) { - vertex_index vi = - parseTriple(token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); + vertex_index vi = parseTriple(token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2)); face.push_back(vi); size_t n = strspn(token, " \t\r"); token += n; @@ -984,10 +977,11 @@ bool LoadObj(std::vector &shapes, // [output] #endif // Create face group per material. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, tags, material, name, true, triangulate); + bool ret = + exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, + material, name, true, triangulate); if (ret) { - shapes.push_back(shape); + shapes.push_back(shape); } shape = shape_t(); faceGroup.clear(); @@ -1015,7 +1009,7 @@ bool LoadObj(std::vector &shapes, // [output] std::string err_mtl; bool ok = readMatFn(namebuf, materials, material_map, err_mtl); err += err_mtl; - + if (!ok) { faceGroup.clear(); // for safety return false; @@ -1028,8 +1022,9 @@ bool LoadObj(std::vector &shapes, // [output] if (token[0] == 'g' && isSpace((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, tags, material, name, true, triangulate); + bool ret = + exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, + material, name, true, triangulate); if (ret) { shapes.push_back(shape); } @@ -1062,8 +1057,9 @@ bool LoadObj(std::vector &shapes, // [output] if (token[0] == 'o' && isSpace((token[1]))) { // flush previous face group. - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, - faceGroup, tags, material, name, true, triangulate); + bool ret = + exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, + material, name, true, triangulate); if (ret) { shapes.push_back(shape); } @@ -1086,50 +1082,47 @@ bool LoadObj(std::vector &shapes, // [output] } if (token[0] == 't' && isSpace(token[1])) { - tag_t tag; + tag_t tag; - char namebuf[4096]; - token += 2; - sscanf(token, "%s", namebuf); - tag.name = std::string(namebuf); + char namebuf[4096]; + token += 2; + sscanf(token, "%s", namebuf); + tag.name = std::string(namebuf); - token += tag.name.size() + 1; + token += tag.name.size() + 1; - tag_sizes ts = parseTagTriple(token); + tag_sizes ts = parseTagTriple(token); - tag.intValues.resize(static_cast(ts.num_ints)); + tag.intValues.resize(static_cast(ts.num_ints)); - for(size_t i = 0; i < static_cast(ts.num_ints); ++i) - { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; - } + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } - tag.floatValues.resize(static_cast(ts.num_floats)); - for(size_t i = 0; i < static_cast(ts.num_floats); ++i) - { - tag.floatValues[i] = parseFloat(token); - token += strcspn(token, "/ \t\r") + 1; - } + tag.floatValues.resize(static_cast(ts.num_floats)); + for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { + tag.floatValues[i] = parseFloat(token); + token += strcspn(token, "/ \t\r") + 1; + } - tag.stringValues.resize(static_cast(ts.num_strings)); - for(size_t i = 0; i < static_cast(ts.num_strings); ++i) - { - char stringValueBuffer[4096]; + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; - sscanf(token, "%s", stringValueBuffer); - tag.stringValues[i] = stringValueBuffer; - token += tag.stringValues[i].size() + 1; - } + sscanf(token, "%s", stringValueBuffer); + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } - tags.push_back(tag); + tags.push_back(tag); } // Ignore unknown command. } - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, - material, name, true, triangulate); + bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, + tags, material, name, true, triangulate); if (ret) { shapes.push_back(shape); } @@ -1141,7 +1134,6 @@ bool LoadObj(std::vector &shapes, // [output] } // namespace - #endif #endif // TINY_OBJ_LOADER_H From 6082a1a4f3183a58023db745089bd4ed8c573484 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 3 Feb 2016 18:49:49 +0900 Subject: [PATCH 070/585] Add osx for Travis build. --- .travis.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 706b8c1b..5b682e7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,19 @@ language: cpp +os: + - linux + - osx + compiler: gcc before_install: - - curl -L "https://dl.bintray.com/gogoprog/gengine/linux64/premake4" -o premake4 - - chmod +x premake4 + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl -L "https://dl.bintray.com/gogoprog/gengine/linux64/premake4" -o premake4; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod +x premake4; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install premake; fi script: - - ./premake4 gmake + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; ./premake4 gmake; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; premake4 gmake; fi - make - ./test_tinyobjloader From 1afdd321311e5643ac726ce12e59608d858f7f75 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 3 Feb 2016 19:34:28 +0900 Subject: [PATCH 071/585] Fix travis script. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5b682e7c..01537667 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install premake; fi script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; ./premake4 gmake; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; premake4 gmake; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./premake4 gmake; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then premake4 gmake; fi - make - ./test_tinyobjloader From 12a55a8f71b28de939aa9372591e84b234c92ede Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 3 Feb 2016 19:36:45 +0900 Subject: [PATCH 072/585] Fix source code layout. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 35cf1006..2444d158 100644 --- a/README.md +++ b/README.md @@ -175,16 +175,16 @@ Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces( size_t indexOffset = 0; for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { - int ngon = shapes[i].mesh.num_vertices[n]; - for (size_t f = 0; f < ngon; f++) { + int ngon = shapes[i].mesh.num_vertices[n]; + for (size_t f = 0; f < ngon; f++) { unsigend int v = shapes[i].mesh.indices[indexOffset + f]; printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, shapes[i].mesh.positions[3*v+0], shapes[i].mesh.positions[3*v+1], shapes[i].mesh.positions[3*v+2]); - } - indexOffset += ngon; + } + indexOffset += ngon; } } From 8f58ef5b1d45e3af485ff585f7e9c8057e4fde12 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 16:50:11 +0900 Subject: [PATCH 073/585] Add coverall to Travis CI. Use cmake to build in Travis script. --- .travis.yml | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01537667..3748f8e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,49 @@ os: - linux - osx +matrix: + include: + # Clang 3.7 + - addons: &clang37 + apt: + sources: + - george-edison55-precise-backports + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + packages: + - cmake + - cmake-data + - ninja-build + - g++-4.9 + - clang-3.7 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug + + - addons: *clang37 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Release + + # Coverage with Clang 3.7 + - addons: *clang37 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0 --coverage" CXXFLAGS="-O0 --coverage" REPORT_COVERAGE=1 + compiler: gcc before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl -L "https://dl.bintray.com/gogoprog/gengine/linux64/premake4" -o premake4; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then chmod +x premake4; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install premake; fi + - if [ -n "$REPORT_COVERAGE" ]; then + pip install --user cpp-coveralls; + fi script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./premake4 gmake; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then premake4 gmake; fi + - mkdir build && cd build + - export CC="${CC}-${COMPILER_VERSION}" + - export CXX="${CXX}-${COMPILER_VERSION}" + - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - make + - cd .. - ./test_tinyobjloader + - if [ -n "$REPORT_COVERAGE" ]; then + coveralls -b . -r . -e examples -e tools -e jni -e python -e images; + fi From 5f7d21afeb71cc07285b86a5eac36d86a117dd04 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 16:57:30 +0900 Subject: [PATCH 074/585] Update Travis script --- .travis.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3748f8e3..2e2417a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ language: cpp +sudo: false os: - linux - osx matrix: + allow_failures: + - os: osx + include: # Clang 3.7 - addons: &clang37 @@ -26,13 +30,29 @@ matrix: compiler: clang env: COMPILER_VERSION=3.7 BUILD_TYPE=Release + # GCC 4.9 + - addons: &gcc49 + apt: + sources: + - george-edison55-precise-backports + - ubuntu-toolchain-r-test + packages: + - cmake + - cmake-data + - ninja-build + - g++-4.9 + compiler: gcc + env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug + + - addons: *gcc49 + compiler: gcc + env: COMPILER_VERSION=4.9 BUILD_TYPE=Release + # Coverage with Clang 3.7 - addons: *clang37 compiler: clang env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0 --coverage" CXXFLAGS="-O0 --coverage" REPORT_COVERAGE=1 -compiler: gcc - before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - if [ -n "$REPORT_COVERAGE" ]; then From 1ae9a8ab4b579ca5923c528fd7c5688f4df88ec2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:01:34 +0900 Subject: [PATCH 075/585] print CC version and cmake version. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2e2417a5..9bcdf0d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,6 +63,8 @@ script: - mkdir build && cd build - export CC="${CC}-${COMPILER_VERSION}" - export CXX="${CXX}-${COMPILER_VERSION}" + - ${CC} -v + - cmake --version - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - make - cd .. From e8418ccc8a599880aa36e1aff3c091fa90222348 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:05:56 +0900 Subject: [PATCH 076/585] Disable osx os for a while. --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9bcdf0d4..b27fce33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,7 @@ language: cpp sudo: false -os: - - linux - - osx - matrix: - allow_failures: - - os: osx - include: # Clang 3.7 - addons: &clang37 From 0889afb2ba763228a6caf2ad57e0db8f7597d9d4 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:19:30 +0900 Subject: [PATCH 077/585] Fix travis script. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b27fce33..59b0659b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,7 +59,7 @@ script: - ${CC} -v - cmake --version - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - - make + - ninja -1 - cd .. - ./test_tinyobjloader - if [ -n "$REPORT_COVERAGE" ]; then From f9e253c2f5c7217b5d41481e0a3457024cf96350 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:29:52 +0900 Subject: [PATCH 078/585] Fix arg for ninja --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59b0659b..bda01e58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,7 +59,7 @@ script: - ${CC} -v - cmake --version - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - - ninja -1 + - ninja - cd .. - ./test_tinyobjloader - if [ -n "$REPORT_COVERAGE" ]; then From 1450980f9167b31cdc3a1898384dcdff45da9624 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:35:34 +0900 Subject: [PATCH 079/585] Fix travis script. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bda01e58..e3c19cb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,8 +60,7 @@ script: - cmake --version - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - ninja - - cd .. - - ./test_tinyobjloader + - ./test_tinyobjloader ../cornell_box.obj - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni -e python -e images; fi From bc737b613beb5b74f1c0275f046723947d1688da Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 17:53:16 +0900 Subject: [PATCH 080/585] Another fix for Travis script. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e3c19cb9..fa1e4356 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ script: - cmake --version - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - ninja - - ./test_tinyobjloader ../cornell_box.obj + - ./test_loader ../cornell_box.obj - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni -e python -e images; fi From 00ba666e7f2f1b217eff83830366c0fe52eec833 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 18:38:41 +0900 Subject: [PATCH 081/585] Fix coverall args. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa1e4356..633e15a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,5 +62,5 @@ script: - ninja - ./test_loader ../cornell_box.obj - if [ -n "$REPORT_COVERAGE" ]; then - coveralls -b . -r . -e examples -e tools -e jni -e python -e images; + coveralls -b . -r . -e examples -e tools -e jni -e python -e images -E ".*CompilerId.*"; fi From 4fa6c29e346f05629479577eba12552af1efaf79 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 11 Feb 2016 18:53:59 +0900 Subject: [PATCH 082/585] Add Coverall badge. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2444d158..69dd3eac 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ tinyobjloader [![Build status](https://ci.appveyor.com/api/projects/status/tlb421q3t2oyobcn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader/branch/master) +[![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master) + http://syoyo.github.io/tinyobjloader/ Tiny but poweful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. From 04a7b206b7a5c7656b7956a3bad09e401c0359d2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 14 Feb 2016 16:40:36 +0900 Subject: [PATCH 083/585] Add github relase setting in travis script. --- .travis.yml | 117 +++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/.travis.yml b/.travis.yml index 633e15a9..60f08fb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,66 +1,63 @@ language: cpp sudo: false - matrix: include: - # Clang 3.7 - - addons: &clang37 - apt: - sources: - - george-edison55-precise-backports - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.7 - packages: - - cmake - - cmake-data - - ninja-build - - g++-4.9 - - clang-3.7 - compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug - - - addons: *clang37 - compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Release - - # GCC 4.9 - - addons: &gcc49 - apt: - sources: - - george-edison55-precise-backports - - ubuntu-toolchain-r-test - packages: - - cmake - - cmake-data - - ninja-build - - g++-4.9 - compiler: gcc - env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug - - - addons: *gcc49 - compiler: gcc - env: COMPILER_VERSION=4.9 BUILD_TYPE=Release - - # Coverage with Clang 3.7 - - addons: *clang37 - compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0 --coverage" CXXFLAGS="-O0 --coverage" REPORT_COVERAGE=1 - + - addons: &1 + apt: + sources: + - george-edison55-precise-backports + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + packages: + - cmake + - cmake-data + - ninja-build + - g++-4.9 + - clang-3.7 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug + - addons: *1 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Release + - addons: &2 + apt: + sources: + - george-edison55-precise-backports + - ubuntu-toolchain-r-test + packages: + - cmake + - cmake-data + - ninja-build + - g++-4.9 + compiler: gcc + env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug + - addons: *2 + compiler: gcc + env: COMPILER_VERSION=4.9 BUILD_TYPE=Release + - addons: *1 + compiler: clang + env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0 --coverage" CXXFLAGS="-O0 + --coverage" REPORT_COVERAGE=1 before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - - if [ -n "$REPORT_COVERAGE" ]; then - pip install --user cpp-coveralls; - fi - +- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi +- if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi script: - - mkdir build && cd build - - export CC="${CC}-${COMPILER_VERSION}" - - export CXX="${CXX}-${COMPILER_VERSION}" - - ${CC} -v - - cmake --version - - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja .. - - ninja - - ./test_loader ../cornell_box.obj - - if [ -n "$REPORT_COVERAGE" ]; then - coveralls -b . -r . -e examples -e tools -e jni -e python -e images -E ".*CompilerId.*"; - fi +- mkdir build && cd build +- export CC="${CC}-${COMPILER_VERSION}" +- export CXX="${CXX}-${COMPILER_VERSION}" +- ${CC} -v +- cmake --version +- cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja + .. +- ninja +- ./test_loader ../cornell_box.obj +- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni + -e python -e images -E ".*CompilerId.*"; fi +deploy: + provider: releases + api_key: + secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= + file: tiny_obj_loader.h + on: + repo: syoyo/tinyobjloader + tags: true From 45b4924c2735b0a94c01e8f3072794dbc5118271 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 14 Feb 2016 17:03:59 +0900 Subject: [PATCH 084/585] Fix travis script. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 60f08fb1..17c8e710 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,7 @@ script: - ./test_loader ../cornell_box.obj - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni -e python -e images -E ".*CompilerId.*"; fi +- cd .. deploy: provider: releases api_key: From 60ba85e9cd74890a53c09ade65e421788c10e0a9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 15 Feb 2016 14:52:26 +0900 Subject: [PATCH 085/585] Add all_branches: true. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 17c8e710..fdbee15f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,6 +59,7 @@ deploy: api_key: secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= file: tiny_obj_loader.h + all_branches: true on: repo: syoyo/tinyobjloader tags: true From ecf1005c72ca6d30a1d8385291ddadbd98071ef3 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 16 Feb 2016 16:50:09 +0900 Subject: [PATCH 086/585] Exclude feature_tests.* from coverall. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fdbee15f..7286151b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,7 @@ script: - ninja - ./test_loader ../cornell_box.obj - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni - -e python -e images -E ".*CompilerId.*"; fi + -e python -e images -E ".*CompilerId.*" -E feature_tests.* ; fi - cd .. deploy: provider: releases From d55863ce50c97ee0f5457d0e3e88a5608b66d4bd Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 16 Feb 2016 16:52:00 +0900 Subject: [PATCH 087/585] Fix coverall basedir argument. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7286151b..ab771379 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,8 +51,8 @@ script: .. - ninja - ./test_loader ../cornell_box.obj -- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r . -e examples -e tools -e jni - -e python -e images -E ".*CompilerId.*" -E feature_tests.* ; fi +- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e jni + -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi - cd .. deploy: provider: releases From 7eb029edaf379bf2935d17e611185830654ac78b Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 17 Feb 2016 16:58:05 +0900 Subject: [PATCH 088/585] Add link to Android Vulkan demo. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 69dd3eac..fb43e0c9 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ TinyObjLoader is successfully used in ... * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront * pbrt-v3 https://https://github.com/mmp/pbrt-v3 * cocos2d-x https://github.com/cocos2d/cocos2d-x/ +* Android Vulkan demo https://github.com/SaschaWillems/Vulkan * Your project here! Features From 9c1826381c067597e17e0b60102cd8b6a1222363 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 19 Feb 2016 12:25:16 +0900 Subject: [PATCH 089/585] Update travis script. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ab771379..f672abc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,3 +63,4 @@ deploy: on: repo: syoyo/tinyobjloader tags: true + skip_cleanup: true From cf52401ca777952952585c7101950e559cb45b32 Mon Sep 17 00:00:00 2001 From: Ambal Date: Sat, 20 Feb 2016 12:09:54 +0200 Subject: [PATCH 090/585] Replace calls to 'isdigit', 'isSpace' and 'isNewLine' functions to macros. Other small performance tweaks. --- tiny_obj_loader.h | 86 +++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 4240c66a..11e5e6ed 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -205,11 +205,9 @@ struct obj_shape { std::vector vt; }; -static inline bool isSpace(const char c) { return (c == ' ') || (c == '\t'); } - -static inline bool isNewLine(const char c) { - return (c == '\r') || (c == '\n') || (c == '\0'); -} +#define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') ) +#define IS_DIGIT( x ) ( (unsigned int)( (x) - '0' ) < (unsigned int)10 ) +#define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') ) // Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { @@ -297,13 +295,13 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { if (*curr == '+' || *curr == '-') { sign = *curr; curr++; - } else if (isdigit(*curr)) { /* Pass through. */ + } else if (IS_DIGIT(*curr)) { /* Pass through. */ } else { goto fail; } // Read the integer part. - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { mantissa *= 10; mantissa += static_cast(*curr - 0x30); curr++; @@ -321,7 +319,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { if (*curr == '.') { curr++; read = 1; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { // NOTE: Don't use powf here, it will absolutely murder precision. mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); read++; @@ -342,14 +340,14 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) { exp_sign = *curr; curr++; - } else if (isdigit(*curr)) { /* Pass through. */ + } else if (IS_DIGIT(*curr)) { /* Pass through. */ } else { // Empty E is not allowed. goto fail; } read = 0; - while ((end_not_reached = (curr != s_end)) && isdigit(*curr)) { + while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { exponent *= 10; exponent += static_cast(*curr - 0x30); curr++; @@ -625,7 +623,7 @@ void LoadMtl(std::map &material_map, continue; // comment line // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { // flush previous material. if (!material.name.empty()) { material_map.insert(std::pair( @@ -649,7 +647,7 @@ void LoadMtl(std::map &material_map, } // ambient - if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -660,7 +658,7 @@ void LoadMtl(std::map &material_map, } // diffuse - if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -671,7 +669,7 @@ void LoadMtl(std::map &material_map, } // specular - if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -682,7 +680,7 @@ void LoadMtl(std::map &material_map, } // transmittance - if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { + if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -693,14 +691,14 @@ void LoadMtl(std::map &material_map, } // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { token += 2; material.ior = parseFloat(token); continue; } // emission - if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { token += 2; float r, g, b; parseFloat3(r, g, b, token); @@ -711,26 +709,26 @@ void LoadMtl(std::map &material_map, } // shininess - if (token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { token += 2; material.shininess = parseFloat(token); continue; } // illum model - if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) { + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { token += 6; material.illum = parseInt(token); continue; } // dissolve - if ((token[0] == 'd' && isSpace(token[1]))) { + if ((token[0] == 'd' && IS_SPACE(token[1]))) { token += 1; material.dissolve = parseFloat(token); continue; } - if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) { + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { token += 2; // Invert value of Tr(assume Tr is in range [0, 1]) material.dissolve = 1.0f - parseFloat(token); @@ -738,56 +736,56 @@ void LoadMtl(std::map &material_map, } // ambient texture - if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { token += 7; material.ambient_texname = token; continue; } // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { token += 7; material.diffuse_texname = token; continue; } // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { token += 7; material.specular_texname = token; continue; } // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { token += 7; material.specular_highlight_texname = token; continue; } // bump texture - if ((0 == strncmp(token, "map_bump", 8)) && isSpace(token[8])) { + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { token += 9; material.bump_texname = token; continue; } // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && isSpace(token[5])) { + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { token += 6; material.alpha_texname = token; continue; } // bump texture - if ((0 == strncmp(token, "bump", 4)) && isSpace(token[4])) { + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { token += 5; material.bump_texname = token; continue; } // displacement texture - if ((0 == strncmp(token, "disp", 4)) && isSpace(token[4])) { + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { token += 5; material.displacement_texname = token; continue; @@ -914,7 +912,7 @@ bool LoadObj(std::vector &shapes, // [output] continue; // comment line // vertex - if (token[0] == 'v' && isSpace((token[1]))) { + if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; float x, y, z; parseFloat3(x, y, z, token); @@ -925,7 +923,7 @@ bool LoadObj(std::vector &shapes, // [output] } // normal - if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { token += 3; float x, y, z; parseFloat3(x, y, z, token); @@ -936,7 +934,7 @@ bool LoadObj(std::vector &shapes, // [output] } // texcoord - if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { token += 3; float x, y; parseFloat2(x, y, token); @@ -946,12 +944,14 @@ bool LoadObj(std::vector &shapes, // [output] } // face - if (token[0] == 'f' && isSpace((token[1]))) { + if (token[0] == 'f' && IS_SPACE((token[1]))) { token += 2; token += strspn(token, " \t"); std::vector face; - while (!isNewLine(token[0])) { + face.reserve(3); + + while (!IS_NEW_LINE(token[0])) { vertex_index vi = parseTriple(token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); @@ -960,13 +960,15 @@ bool LoadObj(std::vector &shapes, // [output] token += n; } - faceGroup.push_back(face); + // replace with emplace_back + std::move on C++11 + faceGroup.push_back(std::vector()); + faceGroup[faceGroup.size() - 1].swap(face); continue; } // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; @@ -997,7 +999,7 @@ bool LoadObj(std::vector &shapes, // [output] } // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER @@ -1019,7 +1021,7 @@ bool LoadObj(std::vector &shapes, // [output] } // group name - if (token[0] == 'g' && isSpace((token[1]))) { + if (token[0] == 'g' && IS_SPACE((token[1]))) { // flush previous face group. bool ret = @@ -1035,7 +1037,9 @@ bool LoadObj(std::vector &shapes, // [output] faceGroup.clear(); std::vector names; - while (!isNewLine(token[0])) { + names.reserve(2); + + while (!IS_NEW_LINE(token[0])) { std::string str = parseString(token); names.push_back(str); token += strspn(token, " \t\r"); // skip tag @@ -1054,7 +1058,7 @@ bool LoadObj(std::vector &shapes, // [output] } // object name - if (token[0] == 'o' && isSpace((token[1]))) { + if (token[0] == 'o' && IS_SPACE((token[1]))) { // flush previous face group. bool ret = @@ -1081,7 +1085,7 @@ bool LoadObj(std::vector &shapes, // [output] continue; } - if (token[0] == 't' && isSpace(token[1])) { + if (token[0] == 't' && IS_SPACE(token[1])) { tag_t tag; char namebuf[4096]; From 0f00a3b3e8d3c12eb4a23948cc0ff1caecf16067 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Mon, 22 Feb 2016 10:04:33 +0900 Subject: [PATCH 091/585] Fixed links. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fb43e0c9..b10b65ef 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,14 @@ Use case TinyObjLoader is successfully used in ... * bullet3 https://github.com/erwincoumans/bullet3 -* pbrt-v2 https://https://github.com/mmp/pbrt-v2 +* pbrt-v2 https://github.com/mmp/pbrt-v2 * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 * mallie https://lighttransport.github.io/mallie * IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ * Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf * Awesome Bump http://awesomebump.besaba.com/about/ * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront -* pbrt-v3 https://https://github.com/mmp/pbrt-v3 +* pbrt-v3 https://github.com/mmp/pbrt-v3 * cocos2d-x https://github.com/cocos2d/cocos2d-x/ * Android Vulkan demo https://github.com/SaschaWillems/Vulkan * Your project here! From 21c93c51eddfa68df003053f470003661e9d5b8e Mon Sep 17 00:00:00 2001 From: Prayag Verma Date: Mon, 22 Feb 2016 09:38:18 +0530 Subject: [PATCH 092/585] Fix a typo and add syntax highlighting language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `poweful` → `powerful` --- README.md | 183 +++++++++++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index b10b65ef..4a063b6f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ tinyobjloader http://syoyo.github.io/tinyobjloader/ -Tiny but poweful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. +Tiny but powerful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse 10M over polygons with moderate memory and time. `tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) @@ -88,106 +88,107 @@ Usage ----- TinyObjLoader triangulate input .obj by default. +```c++ +#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc +#include "tiny_obj_loader.h" - #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc - #include "tiny_obj_loader.h" - - std::string inputfile = "cornell_box.obj"; - std::vector shapes; - std::vector materials; +std::string inputfile = "cornell_box.obj"; +std::vector shapes; +std::vector materials; - std::string err; - bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str()); +std::string err; +bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str()); - if (!err.empty()) { // `err` may contain warning message. - std::cerr << err << std::endl; - } +if (!err.empty()) { // `err` may contain warning message. + std::cerr << err << std::endl; +} - if (!ret) { - exit(1); - } +if (!ret) { + exit(1); +} - std::cout << "# of shapes : " << shapes.size() << std::endl; - std::cout << "# of materials : " << materials.size() << std::endl; +std::cout << "# of shapes : " << shapes.size() << std::endl; +std::cout << "# of materials : " << materials.size() << std::endl; - for (size_t i = 0; i < shapes.size(); i++) { - printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); - } - - printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - assert((shapes[i].mesh.positions.size() % 3) == 0); - for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { - printf(" v[%ld] = (%f, %f, %f)\n", v, - shapes[i].mesh.positions[3*v+0], - shapes[i].mesh.positions[3*v+1], - shapes[i].mesh.positions[3*v+2]); - } - } - - for (size_t i = 0; i < materials.size(); i++) { - printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]); - printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]); - printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]); - printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]); - printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]); - printf(" material.Ns = %f\n", materials[i].shininess); - printf(" material.Ni = %f\n", materials[i].ior); - printf(" material.dissolve = %f\n", materials[i].dissolve); - printf(" material.illum = %d\n", materials[i].illum); - printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); - printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); - printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); - std::map::const_iterator it(materials[i].unknown_parameter.begin()); - std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); - for (; it != itEnd; it++) { - printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); - } - printf("\n"); - } - +for (size_t i = 0; i < shapes.size(); i++) { + printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); + printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); + assert((shapes[i].mesh.indices.size() % 3) == 0); + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); + } + + printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); + assert((shapes[i].mesh.positions.size() % 3) == 0); + for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { + printf(" v[%ld] = (%f, %f, %f)\n", v, + shapes[i].mesh.positions[3*v+0], + shapes[i].mesh.positions[3*v+1], + shapes[i].mesh.positions[3*v+2]); + } +} + +for (size_t i = 0; i < materials.size(); i++) { + printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); + printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]); + printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]); + printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]); + printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]); + printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]); + printf(" material.Ns = %f\n", materials[i].shininess); + printf(" material.Ni = %f\n", materials[i].ior); + printf(" material.dissolve = %f\n", materials[i].dissolve); + printf(" material.illum = %d\n", materials[i].illum); + printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); + printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); + printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); + printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); + std::map::const_iterator it(materials[i].unknown_parameter.begin()); + std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); + for (; it != itEnd; it++) { + printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); + } + printf("\n"); +} +``` Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces(indices). `num_vertices[i]` stores the number of vertices for ith face. +```c++ +#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc +#include "tiny_obj_loader.h" - #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc - #include "tiny_obj_loader.h" - - std::string inputfile = "cornell_box.obj"; - std::vector shapes; - std::vector materials; +std::string inputfile = "cornell_box.obj"; +std::vector shapes; +std::vector materials; - std::string err; - bool triangulate = false; - bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str(), triangulate); +std::string err; +bool triangulate = false; +bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str(), triangulate); - if (!err.empty()) { // `err` may contain warning message. - std::cerr << err << std::endl; - } - - if (!ret) { - exit(1); +if (!err.empty()) { // `err` may contain warning message. + std::cerr << err << std::endl; +} + +if (!ret) { + exit(1); +} + +for (size_t i = 0; i < shapes.size(); i++) { + + size_t indexOffset = 0; + for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { + int ngon = shapes[i].mesh.num_vertices[n]; + for (size_t f = 0; f < ngon; f++) { + unsigend int v = shapes[i].mesh.indices[indexOffset + f]; + printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, + shapes[i].mesh.positions[3*v+0], + shapes[i].mesh.positions[3*v+1], + shapes[i].mesh.positions[3*v+2]); + } + indexOffset += ngon; + } - for (size_t i = 0; i < shapes.size(); i++) { - - size_t indexOffset = 0; - for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { - int ngon = shapes[i].mesh.num_vertices[n]; - for (size_t f = 0; f < ngon; f++) { - unsigend int v = shapes[i].mesh.indices[indexOffset + f]; - printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, - shapes[i].mesh.positions[3*v+0], - shapes[i].mesh.positions[3*v+1], - shapes[i].mesh.positions[3*v+2]); - - } - indexOffset += ngon; - } - - } +} +``` From bc42bc47ad3bca275f1348e88c60d375b3f582d8 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 02:14:52 +0900 Subject: [PATCH 093/585] Don't create new shape by `usemtl`. Fixes #68. --- test.cc | 1 + tiny_obj_loader.h | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test.cc b/test.cc index c2047a94..2539faaa 100644 --- a/test.cc +++ b/test.cc @@ -139,6 +139,7 @@ TestLoadObj( } if (!ret) { + printf("Failed to load/parse .obj.\n"); return false; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 11e5e6ed..4a434984 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -5,6 +5,7 @@ // // +// version 0.9.20: Fixes creating per-face material using `usemtl`(#68) // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) // version 0.9.16: Make tinyobjloader header-only // version 0.9.15: Change API to handle no mtl file case correctly(#58) @@ -978,21 +979,19 @@ bool LoadObj(std::vector &shapes, // [output] sscanf(token, "%s", namebuf); #endif - // Create face group per material. - bool ret = - exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, - material, name, true, triangulate); - if (ret) { - shapes.push_back(shape); - } - shape = shape_t(); - faceGroup.clear(); - + int newMaterialId = -1; if (material_map.find(namebuf) != material_map.end()) { - material = material_map[namebuf]; + newMaterialId = material_map[namebuf]; } else { // { error!! material not found } - material = -1; + } + + if (newMaterialId != material) { + // Create per-face material + exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, + material, name, true, triangulate); + faceGroup.clear(); + material = newMaterialId; } continue; From f2db18dc53211a4e46727d3558f5563b8d5077e2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 16:13:04 +0900 Subject: [PATCH 094/585] Add bintray deploy in travis build. --- .bintray.in | 43 +++++++++++++++++++++++++++++++++++++++ .travis.yml | 23 +++++++++++---------- tools/travis_postbuild.sh | 12 +++++++++++ 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 .bintray.in create mode 100755 tools/travis_postbuild.sh diff --git a/.bintray.in b/.bintray.in new file mode 100644 index 00000000..e38c1db0 --- /dev/null +++ b/.bintray.in @@ -0,0 +1,43 @@ +{ + /* Bintray package information. + In case the package already exists on Bintray, only the name, repo and subject + fields are mandatory. */ + + "package": { + "name": "releases", // Bintray package name + "repo": "tinyobjloader", // Bintray repository name + "subject": "syoyo" // Bintray subject (user or organization) + }, + + /* Package version information. + In case the version already exists on Bintray, only the name fields is mandatory. */ + + "version": { + "name": "@VERSION@", + "desc": "@VERSION@", + "released": "@DATE@", + "vcs_tag": "@VERSION@", + "gpgSign": false + }, + + /* Configure the files you would like to upload to Bintray and their upload path. + You can define one or more groups of patterns. + Each group contains three patterns: + + includePattern: Pattern in the form of Ruby regular expression, indicating the path of files to be uploaded to Bintray. + excludePattern: Optional. Pattern in the form of Ruby regular expression, indicating the path of files to be removed from the list of files specified by the includePattern. + uploadPattern: Upload path on Bintray. The path can contain symbols in the form of $1, $2,... that are replaced with capturing groups defined in the include pattern. + + In the example below, the following files are uploaded, + 1. All gem files located under build/bin/ (including sub directories), + except for files under a the do-not-deploy directory. + The files will be uploaded to Bintray under the gems folder. + 2. All files under build/docs. The files will be uploaded to Bintray under the docs folder. + + Note: Regular expressions defined as part of the includePattern property must be wrapped with brackets. */ + + "files": + [ "tiny_obj_loader.h" ], + "publish": true +} + diff --git a/.travis.yml b/.travis.yml index f672abc8..17684691 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,16 +51,17 @@ script: .. - ninja - ./test_loader ../cornell_box.obj -- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e jni - -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi +- if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e + jni -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi - cd .. + +before_deploy: + - echo "Creating description file for bintray." + - ./tools/travis_postbuild.sh + deploy: - provider: releases - api_key: - secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= - file: tiny_obj_loader.h - all_branches: true - on: - repo: syoyo/tinyobjloader - tags: true - skip_cleanup: true + provider: bintray + file: ".bintray.json" + user: "syoyo" + key: + secure: W4F1VZcDcVOMe8Ymvo0bHery/JSmVhadl1NgAnGus6o7zVw7ChElKA1ho/NtqUbtoW8o1qUKMJdLQeh786jolocZJEJlns9JZ5FCet6H2b3kITfUa4GR5T11V/ZYwL3SajW8vZ1xu5UrpP5HHgFMYtxb1MFrNLDI60sh0RnyV/qFFBnCJGZPagF/M1mzbJeDml5xK5lShH0r8QpH+7MeQ1J8ungEyJ7UCyr1ao8gY9eq1/05IpHR9vri/d48EXQWHbqtI8EwCc7064oCYQGyYcLsD4yPEokwrdelkCvDquSpJLmbJENfZCc4vZGXsykjnQ8+gltJomBAivQFB9vc06ETEJssMzitbrfEZUrqFwZj/HZM7CYGXfGQWltL828SppCjsuWrgQ/VYXM5UgRpmhlxbqnuyxnYvKZ9EDW4+EnMkOmIl7WSDovp8E/4CZ0ghs+YyFS4SrgeqFCXS8bvxrkDUUPSipHuGBOt02fRnccKzU+3zU6Q5fghyLczz4ZtnOdk+Niz/njyF0SZfPYTUgb3GzAJ8Su6kvWJCAGdedON3n1F/TtybCE2dIdATxaO2uFQbwYjSOCiq209oCJ7MrsQZibRsa5a9YXyjlLkPxwOeVwo8wJyJclqWswIkhdSO8xvTnkwESv4yLzLutEOlBVlQbJzpyuS6vx0yHOYkwc= diff --git a/tools/travis_postbuild.sh b/tools/travis_postbuild.sh new file mode 100755 index 00000000..5737d86f --- /dev/null +++ b/tools/travis_postbuild.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +DATEVAL=`date +%b-%d-%Y` +VERSIONVAL=master + +# Use tag as version +if [ $TRAVIS_TAG ]; then + VERSIONVAL=$TRAVIS_TAG +fi + +sed -e s%@DATE@%${DATEVAL}% .bintray.in > .bintray.tmp +sed -e s%@VERSION@%${VERSIONVAL}% .bintray.tmp > .bintray.json From bf131698e51ab214e23f81d1c5f77992813ee309 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 16:20:40 +0900 Subject: [PATCH 095/585] Use Bintray friendly data format. --- tools/travis_postbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/travis_postbuild.sh b/tools/travis_postbuild.sh index 5737d86f..00c5d498 100755 --- a/tools/travis_postbuild.sh +++ b/tools/travis_postbuild.sh @@ -1,6 +1,6 @@ #!/bin/bash -DATEVAL=`date +%b-%d-%Y` +DATEVAL=`date +%Y-%m-%d` VERSIONVAL=master # Use tag as version From 31c66335358f97b9a59d697effed474fe793a3b9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 16:27:58 +0900 Subject: [PATCH 096/585] Fix file patterns in bintray description. --- .bintray.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bintray.in b/.bintray.in index e38c1db0..ba1a949e 100644 --- a/.bintray.in +++ b/.bintray.in @@ -37,7 +37,7 @@ Note: Regular expressions defined as part of the includePattern property must be wrapped with brackets. */ "files": - [ "tiny_obj_loader.h" ], + [ {"includePattern": "(tiny_obj_loader\.h)", "uploadPattern": "$1"} ], "publish": true } From d688bbd9107590039257e4e8509406fd515b61a3 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 17:16:50 +0900 Subject: [PATCH 097/585] Create dist directory to specify uploading files. --- .bintray.in | 8 +------- .travis.yml | 3 +++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.bintray.in b/.bintray.in index ba1a949e..4336d65c 100644 --- a/.bintray.in +++ b/.bintray.in @@ -28,16 +28,10 @@ excludePattern: Optional. Pattern in the form of Ruby regular expression, indicating the path of files to be removed from the list of files specified by the includePattern. uploadPattern: Upload path on Bintray. The path can contain symbols in the form of $1, $2,... that are replaced with capturing groups defined in the include pattern. - In the example below, the following files are uploaded, - 1. All gem files located under build/bin/ (including sub directories), - except for files under a the do-not-deploy directory. - The files will be uploaded to Bintray under the gems folder. - 2. All files under build/docs. The files will be uploaded to Bintray under the docs folder. - Note: Regular expressions defined as part of the includePattern property must be wrapped with brackets. */ "files": - [ {"includePattern": "(tiny_obj_loader\.h)", "uploadPattern": "$1"} ], + [ {"includePattern": "dist/(.*)", "uploadPattern": "$1"} ], "publish": true } diff --git a/.travis.yml b/.travis.yml index 17684691..5f152ae7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,6 +54,9 @@ script: - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e jni -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi - cd .. +- rm -rf dist +- mkdir dist +- cp tiny_obj_loader.h dist/ before_deploy: - echo "Creating description file for bintray." From 06acb38847d02c2039cebc86d0da145f033d0172 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 17:30:06 +0900 Subject: [PATCH 098/585] Resurrect github release deploy. Upload to bintray on specific build(DEPLOY_BUILD=1) --- .travis.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5f152ae7..51544203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ matrix: - g++-4.9 - clang-3.7 compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug + env: DEPLOY_BUILD=1 COMPILER_VERSION=3.7 BUILD_TYPE=Debug - addons: *1 compiler: clang env: COMPILER_VERSION=3.7 BUILD_TYPE=Release @@ -68,3 +68,16 @@ deploy: user: "syoyo" key: secure: W4F1VZcDcVOMe8Ymvo0bHery/JSmVhadl1NgAnGus6o7zVw7ChElKA1ho/NtqUbtoW8o1qUKMJdLQeh786jolocZJEJlns9JZ5FCet6H2b3kITfUa4GR5T11V/ZYwL3SajW8vZ1xu5UrpP5HHgFMYtxb1MFrNLDI60sh0RnyV/qFFBnCJGZPagF/M1mzbJeDml5xK5lShH0r8QpH+7MeQ1J8ungEyJ7UCyr1ao8gY9eq1/05IpHR9vri/d48EXQWHbqtI8EwCc7064oCYQGyYcLsD4yPEokwrdelkCvDquSpJLmbJENfZCc4vZGXsykjnQ8+gltJomBAivQFB9vc06ETEJssMzitbrfEZUrqFwZj/HZM7CYGXfGQWltL828SppCjsuWrgQ/VYXM5UgRpmhlxbqnuyxnYvKZ9EDW4+EnMkOmIl7WSDovp8E/4CZ0ghs+YyFS4SrgeqFCXS8bvxrkDUUPSipHuGBOt02fRnccKzU+3zU6Q5fghyLczz4ZtnOdk+Niz/njyF0SZfPYTUgb3GzAJ8Su6kvWJCAGdedON3n1F/TtybCE2dIdATxaO2uFQbwYjSOCiq209oCJ7MrsQZibRsa5a9YXyjlLkPxwOeVwo8wJyJclqWswIkhdSO8xvTnkwESv4yLzLutEOlBVlQbJzpyuS6vx0yHOYkwc= + on: + condition: -n "$DEPLOY_BUILD" + +deploy: + provider: releases + api_key: + secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= + file: tiny_obj_loader.h + all_branches: true + on: + repo: syoyo/tinyobjloader + tags: true + skip_cleanup: true From 6a25058ef58110074a4991e28ab0815e218827f6 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 17:38:31 +0900 Subject: [PATCH 099/585] Multiple deploy target. --- .travis.yml | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51544203..1ae49eff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,21 +63,19 @@ before_deploy: - ./tools/travis_postbuild.sh deploy: - provider: bintray - file: ".bintray.json" - user: "syoyo" - key: - secure: W4F1VZcDcVOMe8Ymvo0bHery/JSmVhadl1NgAnGus6o7zVw7ChElKA1ho/NtqUbtoW8o1qUKMJdLQeh786jolocZJEJlns9JZ5FCet6H2b3kITfUa4GR5T11V/ZYwL3SajW8vZ1xu5UrpP5HHgFMYtxb1MFrNLDI60sh0RnyV/qFFBnCJGZPagF/M1mzbJeDml5xK5lShH0r8QpH+7MeQ1J8ungEyJ7UCyr1ao8gY9eq1/05IpHR9vri/d48EXQWHbqtI8EwCc7064oCYQGyYcLsD4yPEokwrdelkCvDquSpJLmbJENfZCc4vZGXsykjnQ8+gltJomBAivQFB9vc06ETEJssMzitbrfEZUrqFwZj/HZM7CYGXfGQWltL828SppCjsuWrgQ/VYXM5UgRpmhlxbqnuyxnYvKZ9EDW4+EnMkOmIl7WSDovp8E/4CZ0ghs+YyFS4SrgeqFCXS8bvxrkDUUPSipHuGBOt02fRnccKzU+3zU6Q5fghyLczz4ZtnOdk+Niz/njyF0SZfPYTUgb3GzAJ8Su6kvWJCAGdedON3n1F/TtybCE2dIdATxaO2uFQbwYjSOCiq209oCJ7MrsQZibRsa5a9YXyjlLkPxwOeVwo8wJyJclqWswIkhdSO8xvTnkwESv4yLzLutEOlBVlQbJzpyuS6vx0yHOYkwc= - on: - condition: -n "$DEPLOY_BUILD" - -deploy: - provider: releases - api_key: - secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= - file: tiny_obj_loader.h - all_branches: true - on: - repo: syoyo/tinyobjloader - tags: true - skip_cleanup: true + - provider: bintray + file: ".bintray.json" + user: "syoyo" + key: + secure: W4F1VZcDcVOMe8Ymvo0bHery/JSmVhadl1NgAnGus6o7zVw7ChElKA1ho/NtqUbtoW8o1qUKMJdLQeh786jolocZJEJlns9JZ5FCet6H2b3kITfUa4GR5T11V/ZYwL3SajW8vZ1xu5UrpP5HHgFMYtxb1MFrNLDI60sh0RnyV/qFFBnCJGZPagF/M1mzbJeDml5xK5lShH0r8QpH+7MeQ1J8ungEyJ7UCyr1ao8gY9eq1/05IpHR9vri/d48EXQWHbqtI8EwCc7064oCYQGyYcLsD4yPEokwrdelkCvDquSpJLmbJENfZCc4vZGXsykjnQ8+gltJomBAivQFB9vc06ETEJssMzitbrfEZUrqFwZj/HZM7CYGXfGQWltL828SppCjsuWrgQ/VYXM5UgRpmhlxbqnuyxnYvKZ9EDW4+EnMkOmIl7WSDovp8E/4CZ0ghs+YyFS4SrgeqFCXS8bvxrkDUUPSipHuGBOt02fRnccKzU+3zU6Q5fghyLczz4ZtnOdk+Niz/njyF0SZfPYTUgb3GzAJ8Su6kvWJCAGdedON3n1F/TtybCE2dIdATxaO2uFQbwYjSOCiq209oCJ7MrsQZibRsa5a9YXyjlLkPxwOeVwo8wJyJclqWswIkhdSO8xvTnkwESv4yLzLutEOlBVlQbJzpyuS6vx0yHOYkwc= + on: + condition: -n "$DEPLOY_BUILD" + - provider: releases + api_key: + secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= + file: tiny_obj_loader.h + all_branches: true + on: + repo: syoyo/tinyobjloader + tags: true + skip_cleanup: true From d6cd178d6a4c6f3082cf4731cf7d138f72eba53f Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 12 Mar 2016 17:42:31 +0900 Subject: [PATCH 100/585] Add test data for issue 68. Deploy to Bintray only for the build with tag. --- .travis.yml | 4 + models/usemtl-issue-68.mtl | 9 + models/usemtl-issue-68.obj | 817 +++++++++++++++++++++++++++++++++++++ 3 files changed, 830 insertions(+) create mode 100644 models/usemtl-issue-68.mtl create mode 100644 models/usemtl-issue-68.obj diff --git a/.travis.yml b/.travis.yml index 1ae49eff..bde52f6f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,8 +68,12 @@ deploy: user: "syoyo" key: secure: W4F1VZcDcVOMe8Ymvo0bHery/JSmVhadl1NgAnGus6o7zVw7ChElKA1ho/NtqUbtoW8o1qUKMJdLQeh786jolocZJEJlns9JZ5FCet6H2b3kITfUa4GR5T11V/ZYwL3SajW8vZ1xu5UrpP5HHgFMYtxb1MFrNLDI60sh0RnyV/qFFBnCJGZPagF/M1mzbJeDml5xK5lShH0r8QpH+7MeQ1J8ungEyJ7UCyr1ao8gY9eq1/05IpHR9vri/d48EXQWHbqtI8EwCc7064oCYQGyYcLsD4yPEokwrdelkCvDquSpJLmbJENfZCc4vZGXsykjnQ8+gltJomBAivQFB9vc06ETEJssMzitbrfEZUrqFwZj/HZM7CYGXfGQWltL828SppCjsuWrgQ/VYXM5UgRpmhlxbqnuyxnYvKZ9EDW4+EnMkOmIl7WSDovp8E/4CZ0ghs+YyFS4SrgeqFCXS8bvxrkDUUPSipHuGBOt02fRnccKzU+3zU6Q5fghyLczz4ZtnOdk+Niz/njyF0SZfPYTUgb3GzAJ8Su6kvWJCAGdedON3n1F/TtybCE2dIdATxaO2uFQbwYjSOCiq209oCJ7MrsQZibRsa5a9YXyjlLkPxwOeVwo8wJyJclqWswIkhdSO8xvTnkwESv4yLzLutEOlBVlQbJzpyuS6vx0yHOYkwc= + all_branches: true on: + repo: syoyo/tinyobjloader condition: -n "$DEPLOY_BUILD" + tags: true + skip_cleanup: true - provider: releases api_key: secure: AsXameK4GJn6h6wMmDrKTr7q/o9EI7hX7zWg1W6VaFBQKfkBvOmjJolWimjl6HMoRZ1NpMmK5GDm3zBlTUeABtgVBIyNWgE9vWS39ff6D5iQKcgScFsJkyILt0GikBqbN2pLGQ2t/M1Qh6n1sEIfzqekiCcF5Qvy5yYlYvHtaRGV02QeYAej/xx15/9SMuKTncHhjf63ClYPu8ODid7QUegJUvlQUeXoPsBDbaXMH2uDWoBWF7etX7G2Iob4NE8GX+ZP6dj+Ogi7p4HXThK650mzLL/pUl584EjjY/vePqx0cFhtpiRwvrW8SNPI1aJ1Phwa1enLRUgfS3bnkwQAMw/SCXSK2lnCvkUAXyTgpG03HWrZURj4vhEPXc7qHooO+dsfmi+JanYLaSDyrGpgQznLGjCMnVATimry0KxSufUY8Wt72Wh+nf7N0IgTUCjl32sWnQd/MRZPkxFuaf1h7r9RoH9KZY0yIOV09gABEFCGrOIZA2FcyhC2G26Bc4zyNrfMFpZ2DI76qdcWNdJGkRkpxtH9sGU8JgZu6Em2f1e6+SLgkBsPxbhRk5PwdhA9AXE2p9PmQqhO3jJKusGBZSoHAF7TlwagRY2J01yJxF7ge6zG9U8QuBqs1bB1zdnE34fHWOgs4st3inC+oBDOhvnEg1Nm/qeYVWMBzpwclSg= diff --git a/models/usemtl-issue-68.mtl b/models/usemtl-issue-68.mtl new file mode 100644 index 00000000..24a791eb --- /dev/null +++ b/models/usemtl-issue-68.mtl @@ -0,0 +1,9 @@ +newmtl Material.001 +Ka 0 0 0 +Kd 0 0 0 +Ks 0 0 0 + +newmtl Material.003 +Ka 0 0 0 +Kd 1 1 1 +Ks 0 0 0 diff --git a/models/usemtl-issue-68.obj b/models/usemtl-issue-68.obj new file mode 100644 index 00000000..dddc900e --- /dev/null +++ b/models/usemtl-issue-68.obj @@ -0,0 +1,817 @@ +# https://github.com/syoyo/tinyobjloader/issues/68 +# Blender v2.73 (sub 0) OBJ File: 'enemy.blend' +# www.blender.org +mtllib usemtl-issue-68.mtl +o Cube +v 1.864151 -1.219172 -5.532511 +v 0.575869 -0.666304 5.896140 +v 0.940448 1.000000 -1.971128 +v 1.620345 1.000000 -5.815706 +v 1.864152 1.000000 -6.334323 +v 0.575869 -0.129842 5.896143 +v 5.440438 -1.462153 -5.818601 +v 4.896782 -1.462153 -2.744413 +v 1.000825 -0.677484 1.899605 +v 5.440438 -1.246362 -5.818600 +v 1.000825 0.852342 1.899608 +v 4.896782 -1.246362 -2.744412 +v 1.160660 -0.450871 -2.356325 +v 1.704316 -0.450871 -5.430513 +v 1.000825 -0.351920 -1.293797 +v 1.000825 1.000000 -1.293794 +v 1.160660 -0.877888 -2.356326 +v 1.704316 -0.877888 -5.430514 +v 1.000825 -1.219172 -1.452514 +v 1.000825 1.000000 -1.452511 +v 1.000825 -0.351920 1.759410 +v 1.000825 1.000000 1.759413 +v 9.097919 1.221145 -6.212147 +v 8.356775 1.221145 -2.021231 +v 1.864151 -0.109586 -6.334325 +v 0.575869 -0.398073 5.896141 +v 9.097919 0.943958 -6.212148 +v 8.356775 0.943958 -2.021233 +v 1.061916 0.113661 -1.797961 +v 1.000825 0.161258 1.899606 +v 1.000825 0.324040 -1.293795 +v 1.803060 0.113661 -5.988876 +v 1.000825 -0.109586 -1.452513 +v 1.061916 0.776753 -1.797960 +v 1.803061 0.776753 -5.988875 +v 1.000825 0.324040 1.759412 +v 0.000825 -1.219172 -5.532512 +v 0.000825 -0.666304 5.896139 +v 0.000826 1.000000 -6.334325 +v 0.000825 -0.129842 5.896140 +v 0.000825 0.852342 1.899606 +v 0.000825 -0.677484 1.899604 +v 0.000825 -0.351920 -1.293797 +v 0.000825 1.000000 -1.293796 +v 0.000825 1.000000 -1.452513 +v 0.000825 -1.219172 -1.452515 +v 0.000825 -0.351920 1.759409 +v 0.000825 1.000000 1.759411 +v 0.000826 -0.109586 -6.334326 +v 0.000825 -0.398073 5.896140 +v 0.152918 1.000000 -5.815708 +v 0.152917 1.000000 -1.971130 +v 0.940448 1.168419 -1.971128 +v 1.620345 1.168419 -5.815706 +v 0.152918 1.168419 -5.815708 +v 0.152917 1.168419 -1.971130 +v 0.921118 1.091883 -1.050430 +v 0.921118 1.091883 1.516050 +v 0.080533 1.091883 -1.050432 +v 0.080533 1.091883 1.516048 +v 0.613003 -0.553430 5.546911 +v 0.963691 -0.559956 2.248834 +v 0.613003 -0.396857 5.546912 +v 0.963691 -0.070362 2.248835 +v 1.499370 -0.994317 3.966028 +v 1.850058 -0.997914 0.667950 +v 1.499370 -0.908021 3.966029 +v 1.850058 -0.728071 0.667951 +v 1.601022 0.760960 -6.334324 +v 1.601021 0.129454 -6.334325 +v 0.263955 0.760960 -6.334325 +v 0.263955 0.129454 -6.334325 +v 1.334809 0.760960 -7.515329 +v 1.334809 0.129455 -7.515330 +v 0.530168 0.760960 -7.515330 +v 0.530168 0.129455 -7.515330 +v 1.192720 0.649445 -7.515329 +v 1.192720 0.240971 -7.515330 +v 0.672258 0.649445 -7.515330 +v 0.672258 0.240971 -7.515330 +v 1.192719 0.649444 -6.524630 +v 1.192719 0.240970 -6.524631 +v 0.672257 0.649444 -6.524631 +v 0.672257 0.240970 -6.524631 +v 3.851026 0.431116 -1.883326 +v 3.851026 0.946662 -1.883325 +v 4.592170 0.946662 -6.074241 +v 4.592169 0.431116 -6.074242 +v 4.995714 0.561404 -1.918362 +v 4.995714 1.016394 -1.918360 +v 5.736857 1.016394 -6.109276 +v 5.736857 0.561404 -6.109277 +v 3.975454 0.471731 -2.162156 +v 3.975454 0.919244 -2.162155 +v 4.618796 0.919244 -5.800034 +v 4.618795 0.471730 -5.800035 +v 4.969088 0.584825 -2.192568 +v 4.969088 0.979775 -2.192567 +v 5.612430 0.979775 -5.830446 +v 5.612429 0.584825 -5.830447 +v 0.864214 -0.673890 3.184381 +v 0.864213 0.489129 3.184384 +v 0.864213 -0.018552 3.184383 +v 0.000825 0.489129 3.184382 +v 0.000825 -0.673890 3.184381 +v 0.850955 -0.557858 3.309075 +v 0.850955 -0.175321 3.309076 +v 1.737321 -0.996758 1.728192 +v 1.737321 -0.785920 1.728193 +v -1.864151 -1.219172 -5.532511 +v -0.575869 -0.666304 5.896140 +v -0.940448 1.000000 -1.971128 +v -1.620345 1.000000 -5.815706 +v -1.864152 1.000000 -6.334323 +v -0.575869 -0.129842 5.896143 +v -5.440438 -1.462153 -5.818601 +v -4.896782 -1.462153 -2.744413 +v -1.000825 -0.677484 1.899605 +v -5.440438 -1.246362 -5.818600 +v -1.000825 0.852342 1.899608 +v -4.896782 -1.246362 -2.744412 +v -1.160660 -0.450871 -2.356325 +v -1.704316 -0.450871 -5.430513 +v -1.000825 -0.351920 -1.293797 +v -1.000825 1.000000 -1.293794 +v -1.160660 -0.877888 -2.356326 +v -1.704316 -0.877888 -5.430514 +v -1.000825 -1.219172 -1.452514 +v -1.000825 1.000000 -1.452511 +v -1.000825 -0.351920 1.759410 +v -1.000825 1.000000 1.759413 +v -9.097919 1.221145 -6.212147 +v -8.356775 1.221145 -2.021231 +v -1.864151 -0.109586 -6.334325 +v -0.575869 -0.398073 5.896141 +v -9.097919 0.943958 -6.212148 +v -8.356775 0.943958 -2.021233 +v -1.061916 0.113661 -1.797961 +v -1.000825 0.161258 1.899606 +v -1.000825 0.324040 -1.293795 +v -1.803060 0.113661 -5.988876 +v -1.000825 -0.109586 -1.452513 +v -1.061916 0.776753 -1.797960 +v -1.803061 0.776753 -5.988875 +v -1.000825 0.324040 1.759412 +v -0.000825 -1.219172 -5.532512 +v -0.000825 -0.666304 5.896139 +v -0.000826 1.000000 -6.334325 +v -0.000825 -0.129842 5.896140 +v -0.000825 0.852342 1.899606 +v -0.000825 -0.677484 1.899604 +v -0.000825 -0.351920 -1.293797 +v -0.000825 1.000000 -1.293796 +v -0.000825 1.000000 -1.452513 +v -0.000825 -1.219172 -1.452515 +v -0.000825 -0.351920 1.759409 +v -0.000825 1.000000 1.759411 +v -0.000826 -0.109586 -6.334326 +v -0.000825 -0.398073 5.896140 +v -0.152918 1.000000 -5.815708 +v -0.152917 1.000000 -1.971130 +v -0.940448 1.168419 -1.971128 +v -1.620345 1.168419 -5.815706 +v -0.152918 1.168419 -5.815708 +v -0.152917 1.168419 -1.971130 +v -0.921118 1.091883 -1.050430 +v -0.921118 1.091883 1.516050 +v -0.080533 1.091883 -1.050432 +v -0.080533 1.091883 1.516048 +v -0.613003 -0.553430 5.546911 +v -0.963691 -0.559956 2.248834 +v -0.613003 -0.396857 5.546912 +v -0.963691 -0.070362 2.248835 +v -1.499370 -0.994317 3.966028 +v -1.850058 -0.997914 0.667950 +v -1.499370 -0.908021 3.966029 +v -1.850058 -0.728071 0.667951 +v -1.601022 0.760960 -6.334324 +v -1.601021 0.129454 -6.334325 +v -0.263955 0.760960 -6.334325 +v -0.263955 0.129454 -6.334325 +v -1.334809 0.760960 -7.515329 +v -1.334809 0.129455 -7.515330 +v -0.530168 0.760960 -7.515330 +v -0.530168 0.129455 -7.515330 +v -1.192720 0.649445 -7.515329 +v -1.192720 0.240971 -7.515330 +v -0.672258 0.649445 -7.515330 +v -0.672258 0.240971 -7.515330 +v -1.192719 0.649444 -6.524630 +v -1.192719 0.240970 -6.524631 +v -0.672257 0.649444 -6.524631 +v -0.672257 0.240970 -6.524631 +v -3.851026 0.431116 -1.883326 +v -3.851026 0.946662 -1.883325 +v -4.592170 0.946662 -6.074241 +v -4.592169 0.431116 -6.074242 +v -4.995714 0.561404 -1.918362 +v -4.995714 1.016394 -1.918360 +v -5.736857 1.016394 -6.109276 +v -5.736857 0.561404 -6.109277 +v -3.975454 0.471731 -2.162156 +v -3.975454 0.919244 -2.162155 +v -4.618796 0.919244 -5.800034 +v -4.618795 0.471730 -5.800035 +v -4.969088 0.584825 -2.192568 +v -4.969088 0.979775 -2.192567 +v -5.612430 0.979775 -5.830446 +v -5.612429 0.584825 -5.830447 +v -0.864214 -0.673890 3.184381 +v -0.864213 0.489129 3.184384 +v -0.864213 -0.018552 3.184383 +v -0.000825 0.489129 3.184382 +v -0.000825 -0.673890 3.184381 +v -0.850955 -0.557858 3.309075 +v -0.850955 -0.175321 3.309076 +v -1.737321 -0.996758 1.728192 +v -1.737321 -0.785920 1.728193 +vt 0.135351 -0.558072 +vt 0.003035 -0.363507 +vt 0.092282 -0.976844 +vt -0.081322 0.947351 +vt 0.100058 1.958891 +vt 0.050091 1.852185 +vt -0.092752 1.055565 +vt -0.251711 1.059474 +vt 0.075587 0.041384 +vt -0.086008 0.279003 +vt -0.086212 0.249830 +vt -0.276044 1.968137 +vt -0.246101 1.859467 +vt 0.009828 1.911388 +vt -0.133014 1.114769 +vt 0.413322 1.261595 +vt 0.299103 0.624605 +vt 1.243955 0.407183 +vt 0.515404 1.111487 +vt 1.358173 1.044173 +vt -0.081553 0.914324 +vt 0.080042 0.676706 +vt 0.401185 0.474498 +vt 1.295541 0.331328 +vt 0.365315 1.568841 +vt 0.299111 1.575740 +vt 0.143401 0.707357 +vt 0.629403 1.011947 +vt 0.449192 0.167251 +vt 1.409760 0.968317 +vt 0.986264 1.738667 +vt 1.573373 1.877873 +vt 1.417663 1.009490 +vt 0.237182 -0.196235 +vt 0.721785 1.030226 +vt 0.830554 0.870285 +vt 0.877494 1.898608 +vt 1.351399 1.106930 +vt 0.183935 0.557301 +vt 1.507109 1.975312 +vt 0.241636 0.439088 +vt 0.114297 -0.045011 +vt 0.140593 1.808834 +vt -0.015118 0.940452 +vt 0.156405 -1.071134 +vt 0.164119 -0.998223 +vt 0.040336 -1.068281 +vt 0.104459 -1.162571 +vt -0.165787 1.882802 +vt -0.014821 1.660811 +vt -0.287852 0.283965 +vt -0.293374 0.366508 +vt -0.289630 0.900550 +vt 0.035337 -0.191272 +vt 0.247348 0.172213 +vt 0.253300 1.021193 +vt -0.283166 0.952313 +vt -0.283398 0.919286 +vt 0.039792 0.444050 +vt 0.314806 -0.339851 +vt 0.112962 -0.334889 +vt -0.288056 0.254793 +vt -0.023788 -0.973990 +vt -0.155922 -0.359599 +vt 0.220528 -1.165425 +vt 0.108710 -0.748730 +vt -0.286364 1.918670 +vt -0.291973 1.118678 +vt -0.119962 0.896379 +vt -0.123707 0.362337 +vt 0.162891 -0.598569 +vt 0.467532 -0.853353 +vt 0.201549 -1.053262 +vt 0.161663 -0.198915 +vt 0.267667 -0.752638 +vt 0.278705 -0.371021 +vt 0.526390 -0.542053 +vt 0.483821 -0.479457 +vt 0.488162 -0.883689 +vt 0.500110 -0.105561 +vt 0.564618 -0.200418 +vt -0.110331 2.127229 +vt 0.040636 1.905238 +vt -0.010786 1.578087 +vt 0.104092 1.876168 +vt 0.255058 1.654176 +vt -0.054992 2.087323 +vt 0.203048 1.901245 +vt 0.052081 2.123235 +vt 0.042658 1.943733 +vt -0.056437 1.881175 +vt 0.147710 1.941151 +vt 0.050060 2.084741 +vt 0.146264 1.735002 +vt 0.041212 1.737584 +vt 0.048615 1.878591 +vt 0.663065 1.872485 +vt 0.786311 1.691257 +vt 0.507355 1.004102 +vt 0.630601 0.822874 +vt 0.955144 1.689498 +vt 0.860727 1.828333 +vt 0.725565 1.074543 +vt 0.819981 0.935708 +vt 0.674594 1.805657 +vt 0.539432 1.051867 +vt 0.646413 0.894554 +vt 0.781576 1.648344 +vt 0.240127 -0.712141 +vn 0.994400 0.000000 0.105700 +vn 0.000000 1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn 0.984700 0.000000 0.174100 +vn 0.211800 0.976600 0.037500 +vn -0.103300 0.000000 -0.994600 +vn 0.103300 -0.000000 0.994600 +vn 0.911400 0.378700 0.161200 +vn -0.157300 -0.987200 -0.027800 +vn 0.113700 -0.993300 0.020100 +vn 0.030600 -0.000000 0.999500 +vn -0.061100 0.998100 -0.010800 +vn -0.030600 0.000000 -0.999500 +vn -0.000000 -0.000000 1.000000 +vn 0.000000 0.000000 -1.000000 +vn -0.755400 0.655300 0.000000 +vn 0.000000 -1.000000 0.000000 +vn -0.000000 -0.180000 0.983700 +vn 0.000000 -0.395500 -0.918500 +vn -0.000000 0.688500 0.725200 +vn 0.000000 -0.585700 -0.810500 +vn -0.000000 0.974900 0.222500 +vn -0.000000 -1.000000 0.002800 +vn -1.000000 0.000000 -0.000000 +vn -0.000000 0.935500 0.353200 +vn 0.755400 0.655300 0.000000 +vn 0.000000 0.935500 -0.353200 +vn 0.673800 0.724900 0.143400 +vn 0.872300 -0.000000 0.489100 +vn -0.872300 0.000000 -0.489100 +vn -0.518300 -0.853500 -0.054200 +vn -0.975500 0.000000 -0.219900 +vn 0.975500 0.000000 -0.219900 +vn -0.913200 0.000000 -0.407500 +vn -0.436900 0.896200 -0.077300 +vn -0.995300 -0.000000 0.096600 +vn -0.297300 -0.953400 -0.052600 +vn 0.473900 -0.876600 0.083800 +vn 0.913200 0.000000 0.407500 +vn 0.342200 0.937700 0.060500 +vn 0.995300 -0.000000 -0.096600 +vn -0.519200 -0.853000 -0.054300 +vn 0.722400 0.676400 0.143800 +vn -0.994400 0.000000 0.105700 +vn -0.984700 0.000000 0.174100 +vn -0.211800 0.976600 0.037500 +vn 0.103300 0.000000 -0.994600 +vn -0.103300 -0.000000 0.994600 +vn -0.911400 0.378700 0.161200 +vn 0.157300 -0.987200 -0.027800 +vn -0.113700 -0.993300 0.020100 +vn -0.030600 -0.000000 0.999500 +vn 0.061100 0.998100 -0.010800 +vn 0.030600 0.000000 -0.999500 +vn -0.691900 0.713200 0.112500 +vn -0.872300 -0.000000 0.489100 +vn 0.872300 0.000000 -0.489100 +vn 0.518300 -0.853500 -0.054200 +vn 0.913200 0.000000 -0.407500 +vn 0.436900 0.896200 -0.077300 +vn 0.995300 0.000000 0.096600 +vn 0.297300 -0.953300 -0.052600 +vn -0.473900 -0.876600 0.083800 +vn -0.913200 -0.000000 0.407500 +vn -0.342200 0.937700 0.060500 +vn -0.995300 -0.000000 -0.096600 +vn 0.519200 -0.853000 -0.054300 +vn -0.714800 0.690100 0.113700 +vn 0.974400 0.089700 0.206200 +vn 0.870400 0.288400 0.399100 +vn 0.691900 0.713200 0.112500 +vn -0.518000 -0.853700 -0.053400 +vn -0.519700 -0.852700 -0.053600 +vn 0.714800 0.690100 0.113700 +vn -0.974400 0.089700 0.206200 +vn -0.870400 0.288400 0.399100 +vn -0.673800 0.724900 0.143400 +vn 0.518000 -0.853700 -0.053400 +vn 0.297300 -0.953400 -0.052600 +vn 0.519700 -0.852700 -0.053600 +vn -0.722400 0.676400 0.143800 +vn -0.000000 0.962300 0.272000 +usemtl Material.001 +s off +f 103/1/1 102/2/1 6/3/1 +f 20/4/2 5/5/2 4/6/2 +f 20/4/2 3/7/2 52/8/2 +f 36/9/3 22/10/3 11/11/3 +f 39/12/2 51/13/2 4/6/2 +f 4/6/4 54/14/4 53/15/4 +f 14/16/5 13/17/5 12/18/5 +f 18/19/6 14/16/6 10/20/6 +f 20/4/3 16/21/3 31/22/3 +f 17/23/7 8/24/7 12/18/7 +f 25/25/4 32/26/4 29/27/4 +f 10/20/4 12/18/4 8/24/4 +f 1/28/8 18/19/8 17/23/8 +f 19/29/4 17/23/4 13/17/4 +f 25/25/4 14/16/4 18/19/4 +f 18/19/9 7/30/9 8/24/9 +f 92/31/10 27/32/10 28/33/10 +f 16/21/3 22/10/3 36/9/3 +f 31/22/3 36/9/3 21/34/3 +f 90/35/11 89/36/11 28/33/11 +f 91/37/12 90/35/12 24/38/12 +f 33/39/4 13/17/4 14/16/4 +f 23/40/4 24/38/4 28/33/4 +f 33/39/3 31/22/3 15/41/3 +f 21/34/3 36/9/3 30/42/3 +f 5/5/4 35/43/4 32/26/4 +f 5/5/4 20/4/4 34/44/4 +f 33/39/4 29/27/4 34/44/4 +f 91/37/13 23/40/13 27/32/13 +f 103/1/1 26/45/1 63/46/1 +f 26/45/14 50/47/14 38/48/14 +f 39/12/15 71/49/15 72/50/15 +f 48/51/16 60/52/16 59/53/16 +f 15/41/17 21/34/17 47/54/17 +f 19/29/17 46/55/17 37/56/17 +f 39/12/2 45/57/2 52/8/2 +f 20/4/2 45/57/2 44/58/2 +f 19/29/18 15/41/18 43/59/18 +f 9/60/19 42/61/19 47/54/19 +f 22/10/20 48/51/20 41/62/20 +f 25/25/21 1/28/21 37/56/21 +f 6/3/14 40/63/14 50/47/14 +f 104/64/22 40/63/22 6/3/22 +f 2/65/23 38/48/23 105/66/23 +f 55/67/2 56/68/2 53/15/2 +f 3/7/14 53/15/14 56/68/14 +f 51/13/15 55/67/15 54/14/15 +f 52/8/24 56/68/24 55/67/24 +f 57/69/2 59/53/2 60/52/2 +f 48/51/25 22/10/25 58/70/25 +f 16/21/26 57/69/26 58/70/26 +f 16/21/27 44/58/27 59/53/27 +f 107/71/28 63/46/28 67/72/28 +f 26/45/1 2/65/1 61/73/1 +f 9/60/1 30/42/1 64/74/1 +f 101/75/1 9/60/1 62/76/1 +f 108/77/1 109/78/1 67/72/1 +f 61/73/29 65/79/29 67/72/29 +f 62/76/30 64/74/30 68/80/30 +f 62/76/31 66/81/31 108/77/31 +f 71/49/32 75/82/32 76/83/32 +f 25/25/15 49/84/15 72/50/15 +f 5/5/15 69/85/15 71/49/15 +f 25/25/15 70/86/15 69/85/15 +f 76/83/15 75/82/15 79/87/15 +f 72/50/17 76/83/17 74/88/17 +f 71/49/2 69/85/2 73/89/2 +f 70/86/33 74/88/33 73/89/33 +f 80/90/3 79/87/3 83/91/3 +f 76/83/15 80/90/15 78/92/15 +f 75/82/15 73/89/15 77/93/15 +f 74/88/15 78/92/15 77/93/15 +f 82/94/15 84/95/15 83/91/15 +f 80/90/2 84/95/2 82/94/2 +f 77/93/17 81/96/17 83/91/17 +f 77/93/24 78/92/24 82/94/24 +f 35/43/13 87/97/13 88/98/13 +f 35/43/12 34/44/12 86/99/12 +f 34/44/11 29/27/11 85/100/11 +f 32/26/10 88/98/10 85/100/10 +f 92/31/34 100/101/34 99/102/34 +f 90/35/35 91/37/35 99/102/35 +f 89/36/36 90/35/36 98/103/36 +f 89/36/37 97/104/37 100/101/37 +f 95/105/13 99/102/13 100/101/13 +f 95/105/12 94/106/12 98/103/12 +f 94/106/11 93/107/11 97/104/11 +f 96/108/10 100/101/10 97/104/10 +f 88/98/38 96/108/38 93/107/38 +f 86/99/39 85/100/39 93/107/39 +f 87/97/40 86/99/40 94/106/40 +f 87/97/41 95/105/41 96/108/41 +f 106/109/42 108/77/42 65/79/42 +f 66/81/1 68/80/1 109/78/1 +f 101/75/1 106/109/1 61/73/1 +f 64/74/43 107/71/43 109/78/43 +f 101/75/23 105/66/23 42/61/23 +f 103/1/1 107/71/1 64/74/1 +f 30/42/1 11/11/1 102/2/1 +f 212/1/44 135/45/44 115/3/44 +f 129/4/2 112/7/2 113/6/2 +f 161/8/2 112/7/2 129/4/2 +f 145/9/24 139/42/24 120/11/24 +f 113/6/2 160/13/2 148/12/2 +f 162/15/45 163/14/45 113/6/45 +f 123/16/46 119/20/46 121/18/46 +f 127/19/47 116/30/47 119/20/47 +f 140/22/24 125/21/24 129/4/24 +f 121/18/48 117/24/48 126/23/48 +f 138/27/45 141/26/45 134/25/45 +f 117/24/45 121/18/45 119/20/45 +f 126/23/49 127/19/49 110/28/49 +f 122/17/45 126/23/45 128/29/45 +f 127/19/45 123/16/45 134/25/45 +f 117/24/50 116/30/50 127/19/50 +f 137/33/51 136/32/51 201/31/51 +f 145/9/24 131/10/24 125/21/24 +f 130/34/24 145/9/24 140/22/24 +f 199/35/52 133/38/52 137/33/52 +f 200/37/53 132/40/53 133/38/53 +f 123/16/45 122/17/45 142/39/45 +f 137/33/45 133/38/45 132/40/45 +f 124/41/24 140/22/24 142/39/24 +f 130/34/24 118/60/24 139/42/24 +f 141/26/45 144/43/45 114/5/45 +f 114/5/45 144/43/45 143/44/45 +f 143/44/45 138/27/45 142/39/45 +f 136/32/54 132/40/54 200/37/54 +f 212/1/44 216/71/44 172/46/44 +f 147/48/14 159/47/14 135/45/14 +f 181/50/15 180/49/15 148/12/15 +f 168/53/26 169/52/26 157/51/26 +f 124/41/17 152/59/17 156/54/17 +f 146/56/17 155/55/17 128/29/17 +f 148/12/2 160/13/2 161/8/2 +f 129/4/2 125/21/2 153/58/2 +f 155/55/18 152/59/18 124/41/18 +f 130/34/19 156/54/19 151/61/19 +f 131/10/20 120/11/20 150/62/20 +f 134/25/21 158/84/21 146/56/21 +f 159/47/14 149/63/14 115/3/14 +f 115/3/22 149/63/22 213/64/22 +f 214/66/23 147/48/23 111/65/23 +f 162/15/2 165/68/2 164/67/2 +f 165/68/14 162/15/14 112/7/14 +f 163/14/15 164/67/15 160/13/15 +f 164/67/3 165/68/3 161/8/3 +f 166/69/2 167/70/2 169/52/2 +f 157/51/25 169/52/25 167/70/25 +f 167/70/16 166/69/16 125/21/16 +f 125/21/27 166/69/27 168/53/27 +f 216/71/55 218/78/55 176/72/55 +f 135/45/44 172/46/44 170/73/44 +f 118/60/44 171/76/44 173/74/44 +f 210/75/44 215/109/44 171/76/44 +f 217/77/44 174/79/44 176/72/44 +f 176/72/56 174/79/56 170/73/56 +f 171/76/57 175/81/57 177/80/57 +f 217/77/58 175/81/58 171/76/58 +f 185/83/33 184/82/33 180/49/33 +f 134/25/15 179/86/15 181/50/15 +f 180/49/15 178/85/15 114/5/15 +f 178/85/15 179/86/15 134/25/15 +f 189/90/15 188/87/15 184/82/15 +f 183/88/17 185/83/17 181/50/17 +f 180/49/2 184/82/2 182/89/2 +f 182/89/32 183/88/32 179/86/32 +f 189/90/24 193/95/24 192/91/24 +f 187/92/15 189/90/15 185/83/15 +f 184/82/15 188/87/15 186/93/15 +f 186/93/15 187/92/15 183/88/15 +f 192/91/15 193/95/15 191/94/15 +f 191/94/2 193/95/2 189/90/2 +f 192/91/17 190/96/17 186/93/17 +f 186/93/3 190/96/3 191/94/3 +f 197/98/54 196/97/54 144/43/54 +f 144/43/53 196/97/53 195/99/53 +f 143/44/52 195/99/52 194/100/52 +f 194/100/51 197/98/51 141/26/51 +f 208/102/59 209/101/59 201/31/59 +f 199/35/60 207/103/60 208/102/60 +f 198/36/61 206/104/61 207/103/61 +f 209/101/62 206/104/62 198/36/62 +f 209/101/54 208/102/54 204/105/54 +f 204/105/53 208/102/53 207/103/53 +f 203/106/52 207/103/52 206/104/52 +f 206/104/51 209/101/51 205/108/51 +f 202/107/63 205/108/63 197/98/63 +f 195/99/64 203/106/64 202/107/64 +f 196/97/65 204/105/65 203/106/65 +f 205/108/66 204/105/66 196/97/66 +f 174/79/67 217/77/67 215/109/67 +f 175/81/44 217/77/44 218/78/44 +f 170/73/44 215/109/44 210/75/44 +f 173/74/68 177/80/68 218/78/68 +f 151/61/23 214/66/23 210/75/23 +f 173/74/44 216/71/44 212/1/44 +f 139/42/44 212/1/44 211/2/44 +f 26/45/1 103/1/1 6/3/1 +f 3/7/2 20/4/2 4/6/2 +f 45/57/2 20/4/2 52/8/2 +f 30/42/3 36/9/3 11/11/3 +f 5/5/2 39/12/2 4/6/2 +f 3/7/4 4/6/4 53/15/4 +f 10/20/5 14/16/5 12/18/5 +f 7/30/6 18/19/6 10/20/6 +f 33/39/3 20/4/3 31/22/3 +f 13/17/7 17/23/7 12/18/7 +f 33/39/4 25/25/4 29/27/4 +f 7/30/4 10/20/4 8/24/4 +f 19/29/69 1/28/69 17/23/69 +f 33/39/4 19/29/4 13/17/4 +f 1/28/70 25/25/70 18/19/70 +f 17/23/9 18/19/9 8/24/9 +f 89/36/10 92/31/10 28/33/10 +f 31/22/3 16/21/3 36/9/3 +f 15/41/3 31/22/3 21/34/3 +f 24/38/11 90/35/11 28/33/11 +f 23/40/12 91/37/12 24/38/12 +f 25/25/4 33/39/4 14/16/4 +f 27/32/4 23/40/4 28/33/4 +f 19/29/3 33/39/3 15/41/3 +f 9/60/3 21/34/3 30/42/3 +f 25/25/4 5/5/4 32/26/4 +f 35/43/4 5/5/4 34/44/4 +f 20/4/4 33/39/4 34/44/4 +f 92/31/13 91/37/13 27/32/13 +f 107/71/1 103/1/1 63/46/1 +f 2/65/14 26/45/14 38/48/14 +f 49/84/15 39/12/15 72/50/15 +f 44/58/16 48/51/16 59/53/16 +f 43/59/17 15/41/17 47/54/17 +f 1/28/17 19/29/17 37/56/17 +f 51/13/2 39/12/2 52/8/2 +f 16/21/2 20/4/2 44/58/2 +f 46/55/18 19/29/18 43/59/18 +f 21/34/19 9/60/19 47/54/19 +f 11/11/20 22/10/20 41/62/20 +f 49/84/21 25/25/21 37/56/21 +f 26/45/14 6/3/14 50/47/14 +f 102/2/22 104/64/22 6/3/22 +f 101/75/23 2/65/23 105/66/23 +f 54/14/2 55/67/2 53/15/2 +f 52/8/14 3/7/14 56/68/14 +f 4/6/15 51/13/15 54/14/15 +f 51/13/24 52/8/24 55/67/24 +f 58/70/2 57/69/2 60/52/2 +f 60/52/25 48/51/25 58/70/25 +f 22/10/26 16/21/26 58/70/26 +f 57/69/27 16/21/27 59/53/27 +f 109/78/71 107/71/71 67/72/71 +f 63/46/1 26/45/1 61/73/1 +f 62/76/1 9/60/1 64/74/1 +f 106/109/1 101/75/1 62/76/1 +f 65/79/1 108/77/1 67/72/1 +f 63/46/29 61/73/29 67/72/29 +f 66/81/30 62/76/30 68/80/30 +f 106/109/72 62/76/72 108/77/72 +f 72/50/32 71/49/32 76/83/32 +f 70/86/15 25/25/15 72/50/15 +f 39/12/15 5/5/15 71/49/15 +f 5/5/15 25/25/15 69/85/15 +f 80/90/15 76/83/15 79/87/15 +f 70/86/17 72/50/17 74/88/17 +f 75/82/2 71/49/2 73/89/2 +f 69/85/33 70/86/33 73/89/33 +f 84/95/3 80/90/3 83/91/3 +f 74/88/15 76/83/15 78/92/15 +f 79/87/15 75/82/15 77/93/15 +f 73/89/15 74/88/15 77/93/15 +f 81/96/15 82/94/15 83/91/15 +f 78/92/2 80/90/2 82/94/2 +f 79/87/17 77/93/17 83/91/17 +f 81/96/24 77/93/24 82/94/24 +f 32/26/13 35/43/13 88/98/13 +f 87/97/12 35/43/12 86/99/12 +f 86/99/11 34/44/11 85/100/11 +f 29/27/10 32/26/10 85/100/10 +f 91/37/34 92/31/34 99/102/34 +f 98/103/35 90/35/35 99/102/35 +f 97/104/36 89/36/36 98/103/36 +f 92/31/37 89/36/37 100/101/37 +f 96/108/13 95/105/13 100/101/13 +f 99/102/12 95/105/12 98/103/12 +f 98/103/11 94/106/11 97/104/11 +f 93/107/10 96/108/10 97/104/10 +f 85/100/38 88/98/38 93/107/38 +f 94/106/39 86/99/39 93/107/39 +f 95/105/40 87/97/40 94/106/40 +f 88/98/41 87/97/41 96/108/41 +f 61/73/73 106/109/73 65/79/73 +f 108/77/1 66/81/1 109/78/1 +f 2/65/1 101/75/1 61/73/1 +f 68/80/74 64/74/74 109/78/74 +f 9/60/23 101/75/23 42/61/23 +f 30/42/1 103/1/1 64/74/1 +f 103/1/1 30/42/1 102/2/1 +f 211/2/44 212/1/44 115/3/44 +f 114/5/2 129/4/2 113/6/2 +f 154/57/2 161/8/2 129/4/2 +f 131/10/24 145/9/24 120/11/24 +f 114/5/2 113/6/2 148/12/2 +f 112/7/45 162/15/45 113/6/45 +f 122/17/46 123/16/46 121/18/46 +f 123/16/47 127/19/47 119/20/47 +f 142/39/24 140/22/24 129/4/24 +f 122/17/48 121/18/48 126/23/48 +f 142/39/45 138/27/45 134/25/45 +f 116/30/45 117/24/45 119/20/45 +f 128/29/75 126/23/75 110/28/75 +f 142/39/45 122/17/45 128/29/45 +f 110/28/76 127/19/76 134/25/76 +f 126/23/50 117/24/50 127/19/50 +f 198/36/51 137/33/51 201/31/51 +f 140/22/24 145/9/24 125/21/24 +f 124/41/24 130/34/24 140/22/24 +f 198/36/52 199/35/52 137/33/52 +f 199/35/53 200/37/53 133/38/53 +f 134/25/45 123/16/45 142/39/45 +f 136/32/45 137/33/45 132/40/45 +f 128/29/24 124/41/24 142/39/24 +f 145/9/24 130/34/24 139/42/24 +f 134/25/45 141/26/45 114/5/45 +f 129/4/45 114/5/45 143/44/45 +f 129/4/45 143/44/45 142/39/45 +f 201/31/54 136/32/54 200/37/54 +f 135/45/44 212/1/44 172/46/44 +f 111/65/14 147/48/14 135/45/14 +f 158/84/15 181/50/15 148/12/15 +f 153/58/26 168/53/26 157/51/26 +f 130/34/17 124/41/17 156/54/17 +f 110/28/17 146/56/17 128/29/17 +f 154/57/2 148/12/2 161/8/2 +f 154/57/2 129/4/2 153/58/2 +f 128/29/18 155/55/18 124/41/18 +f 118/60/19 130/34/19 151/61/19 +f 157/51/20 131/10/20 150/62/20 +f 110/28/21 134/25/21 146/56/21 +f 135/45/14 159/47/14 115/3/14 +f 211/2/22 115/3/22 213/64/22 +f 210/75/23 214/66/23 111/65/23 +f 163/14/2 162/15/2 164/67/2 +f 161/8/14 165/68/14 112/7/14 +f 113/6/15 163/14/15 160/13/15 +f 160/13/3 164/67/3 161/8/3 +f 168/53/2 166/69/2 169/52/2 +f 131/10/25 157/51/25 167/70/25 +f 131/10/16 167/70/16 125/21/16 +f 153/58/27 125/21/27 168/53/27 +f 172/46/77 216/71/77 176/72/77 +f 111/65/44 135/45/44 170/73/44 +f 139/42/44 118/60/44 173/74/44 +f 118/60/44 210/75/44 171/76/44 +f 218/78/44 217/77/44 176/72/44 +f 172/46/56 176/72/56 170/73/56 +f 173/74/57 171/76/57 177/80/57 +f 215/109/78 217/77/78 171/76/78 +f 181/50/33 185/83/33 180/49/33 +f 158/84/15 134/25/15 181/50/15 +f 148/12/15 180/49/15 114/5/15 +f 114/5/15 178/85/15 134/25/15 +f 185/83/15 189/90/15 184/82/15 +f 179/86/17 183/88/17 181/50/17 +f 178/85/2 180/49/2 182/89/2 +f 178/85/32 182/89/32 179/86/32 +f 188/87/24 189/90/24 192/91/24 +f 183/88/15 187/92/15 185/83/15 +f 182/89/15 184/82/15 186/93/15 +f 182/89/15 186/93/15 183/88/15 +f 190/96/15 192/91/15 191/94/15 +f 187/92/2 191/94/2 189/90/2 +f 188/87/17 192/91/17 186/93/17 +f 187/92/3 186/93/3 191/94/3 +f 141/26/54 197/98/54 144/43/54 +f 143/44/53 144/43/53 195/99/53 +f 138/27/52 143/44/52 194/100/52 +f 138/27/51 194/100/51 141/26/51 +f 200/37/59 208/102/59 201/31/59 +f 200/37/60 199/35/60 208/102/60 +f 199/35/61 198/36/61 207/103/61 +f 201/31/79 209/101/79 198/36/79 +f 205/108/54 209/101/54 204/105/54 +f 203/106/53 204/105/53 207/103/53 +f 202/107/52 203/106/52 206/104/52 +f 202/107/51 206/104/51 205/108/51 +f 194/100/63 202/107/63 197/98/63 +f 194/100/64 195/99/64 202/107/64 +f 195/99/65 196/97/65 203/106/65 +f 197/98/66 205/108/66 196/97/66 +f 170/73/80 174/79/80 215/109/80 +f 177/80/44 175/81/44 218/78/44 +f 111/65/44 170/73/44 210/75/44 +f 216/71/81 173/74/81 218/78/81 +f 118/60/23 151/61/23 210/75/23 +f 139/42/44 173/74/44 212/1/44 +f 120/11/44 139/42/44 211/2/44 +usemtl Material.003 +f 41/62/82 104/64/82 102/2/82 +f 211/2/82 213/64/82 150/62/82 +f 11/11/82 41/62/82 102/2/82 +f 120/11/82 211/2/82 150/62/82 From ad9911ef1b124c628581c0f75adb3178f9d03c3c Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 25 Mar 2016 18:43:52 +0900 Subject: [PATCH 101/585] Initialize vertex_index. Fixes #70. --- tiny_obj_loader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 4a434984..826ec24e 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -175,8 +175,8 @@ MaterialReader::~MaterialReader() {} struct vertex_index { int v_idx, vt_idx, vn_idx; - vertex_index() {} - vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} vertex_index(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} }; From b40e8c942764e4923d6ef31767ea2949b8a1e22b Mon Sep 17 00:00:00 2001 From: Nicolas Guillemot Date: Sat, 2 Apr 2016 15:15:29 -0700 Subject: [PATCH 102/585] use sscanf_s in MSVC consistent with other uses of sscanf in the library --- tiny_obj_loader.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 826ec24e..b039c40e 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1089,7 +1089,11 @@ bool LoadObj(std::vector &shapes, // [output] char namebuf[4096]; token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else sscanf(token, "%s", namebuf); +#endif tag.name = std::string(namebuf); token += tag.name.size() + 1; @@ -1113,7 +1117,11 @@ bool LoadObj(std::vector &shapes, // [output] for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { char stringValueBuffer[4096]; +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, (unsigned)_countof(stringValueBuffer)); +#else sscanf(token, "%s", stringValueBuffer); +#endif tag.stringValues[i] = stringValueBuffer; token += tag.stringValues[i].size() + 1; } From 1b24514ed9e07922df0f4f02a184e760852a5416 Mon Sep 17 00:00:00 2001 From: Jamie Wong Date: Tue, 12 Apr 2016 22:36:56 +0800 Subject: [PATCH 103/585] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a063b6f..8bc365a1 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ for (size_t i = 0; i < shapes.size(); i++) { for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { int ngon = shapes[i].mesh.num_vertices[n]; for (size_t f = 0; f < ngon; f++) { - unsigend int v = shapes[i].mesh.indices[indexOffset + f]; + unsigned int v = shapes[i].mesh.indices[indexOffset + f]; printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, shapes[i].mesh.positions[3*v+0], shapes[i].mesh.positions[3*v+1], From d119dcb9765cbee205448125c96c86546bdd13f1 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Fri, 15 Apr 2016 12:31:47 +0900 Subject: [PATCH 104/585] Cosmetics. --- tiny_obj_loader.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index b039c40e..8bdd0a32 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -38,8 +38,8 @@ // #include "tiny_obj_loader.h" // -#ifndef TINY_OBJ_LOADER_H -#define TINY_OBJ_LOADER_H +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ #include #include @@ -159,9 +159,6 @@ void LoadMtl(std::map &material_map, // [output] #include #include -#include -#include -#include #include #include @@ -1147,4 +1144,4 @@ bool LoadObj(std::vector &shapes, // [output] #endif -#endif // TINY_OBJ_LOADER_H +#endif // TINY_OBJ_LOADER_H_ From ee7d6cc0fdae6a252fcd2a94a28647381c42239b Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 16 Apr 2016 19:49:12 +0900 Subject: [PATCH 105/585] Refactor and re-design tinyobjloader. * Separete attribs(vtx,normal,texcoords) and shape. * Support different index for vtx/normal/texcoord. --- README.md | 1 + .../catmark_torus_creases0.obj | 0 .../cornell_box_multimaterial.obj | 0 cube.mtl => models/cube.mtl | 0 cube.obj => models/cube.obj | 0 .../missing_material_file.obj | 0 no_material.obj => models/no_material.obj | 0 test-nan.obj => models/test-nan.obj | 0 test.cc | 74 ++++-- tiny_obj_loader.h | 242 +++++++++--------- 10 files changed, 171 insertions(+), 146 deletions(-) rename catmark_torus_creases0.obj => models/catmark_torus_creases0.obj (100%) rename cornell_box_multimaterial.obj => models/cornell_box_multimaterial.obj (100%) rename cube.mtl => models/cube.mtl (100%) rename cube.obj => models/cube.obj (100%) rename missing_material_file.obj => models/missing_material_file.obj (100%) rename no_material.obj => models/no_material.obj (100%) rename test-nan.obj => models/test-nan.obj (100%) diff --git a/README.md b/README.md index 8bc365a1..056f7c3a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Tiny but powerful single file wavefront obj loader written in C++. No dependency What's new ---------- +* XX YY, ZZZZ : New data strcutre and API! * Jan 29, 2016 : Support n-polygon(no triangulation) and OpenSubdiv crease tag! Thanks dboogert! * Nov 26, 2015 : Now single-header only!. * Nov 08, 2015 : Improved API. diff --git a/catmark_torus_creases0.obj b/models/catmark_torus_creases0.obj similarity index 100% rename from catmark_torus_creases0.obj rename to models/catmark_torus_creases0.obj diff --git a/cornell_box_multimaterial.obj b/models/cornell_box_multimaterial.obj similarity index 100% rename from cornell_box_multimaterial.obj rename to models/cornell_box_multimaterial.obj diff --git a/cube.mtl b/models/cube.mtl similarity index 100% rename from cube.mtl rename to models/cube.mtl diff --git a/cube.obj b/models/cube.obj similarity index 100% rename from cube.obj rename to models/cube.obj diff --git a/missing_material_file.obj b/models/missing_material_file.obj similarity index 100% rename from missing_material_file.obj rename to models/missing_material_file.obj diff --git a/no_material.obj b/models/no_material.obj similarity index 100% rename from no_material.obj rename to models/no_material.obj diff --git a/test-nan.obj b/models/test-nan.obj similarity index 100% rename from test-nan.obj rename to models/test-nan.obj diff --git a/test.cc b/test.cc index 2539faaa..88d8be18 100644 --- a/test.cc +++ b/test.cc @@ -8,11 +8,35 @@ #include #include -static void PrintInfo(const std::vector& shapes, const std::vector& materials, bool triangulate = true) +static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials, bool triangulate = true) { + std::cout << "# of positions : " << (attrib.positions.size() / 3) << std::endl; + std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; + std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; + std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl; + for (size_t v = 0; v < attrib.positions.size() / 3; v++) { + printf(" v[%ld] = (%f, %f, %f)\n", v, + static_cast(attrib.positions[3*v+0]), + static_cast(attrib.positions[3*v+1]), + static_cast(attrib.positions[3*v+2])); + } + + for (size_t v = 0; v < attrib.normals.size() / 3; v++) { + printf(" n[%ld] = (%f, %f, %f)\n", v, + static_cast(attrib.normals[3*v+0]), + static_cast(attrib.normals[3*v+1]), + static_cast(attrib.normals[3*v+2])); + } + + for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { + printf(" uv[%ld] = (%f, %f)\n", v, + static_cast(attrib.texcoords[2*v+0]), + static_cast(attrib.texcoords[2*v+1])); + } + for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); @@ -22,11 +46,19 @@ static void PrintInfo(const std::vector& shapes, const std::ve printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); assert((shapes[i].mesh.indices.size() % 3) == 0); for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); + tinyobj::index_t i0 = shapes[i].mesh.indices[3*f+0]; + tinyobj::index_t i1 = shapes[i].mesh.indices[3*f+1]; + tinyobj::index_t i2 = shapes[i].mesh.indices[3*f+2]; + printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, + i0.vertex_index, i0.normal_index, i0.texcoord_index, + i1.vertex_index, i1.normal_index, i1.texcoord_index, + i2.vertex_index, i2.normal_index, i2.texcoord_index, + shapes[i].mesh.material_ids[f]); } } else { for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { - printf(" idx[%ld] = %d\n", f, shapes[i].mesh.indices[f]); + tinyobj::index_t idx = shapes[i].mesh.indices[f]; + printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, idx.texcoord_index); } printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); @@ -44,14 +76,14 @@ static void PrintInfo(const std::vector& shapes, const std::ve static_cast(shapes[i].mesh.num_vertices[v])); } - printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - assert((shapes[i].mesh.positions.size() % 3) == 0); - for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { - printf(" v[%ld] = (%f, %f, %f)\n", v, - shapes[i].mesh.positions[3*v+0], - shapes[i].mesh.positions[3*v+1], - shapes[i].mesh.positions[3*v+2]); - } + //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); + //assert((shapes[i].mesh.positions.size() % 3) == 0); + //for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { + // printf(" v[%ld] = (%f, %f, %f)\n", v, + // static_cast(shapes[i].mesh.positions[3*v+0]), + // static_cast(shapes[i].mesh.positions[3*v+1]), + // static_cast(shapes[i].mesh.positions[3*v+2])); + //} printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size()); for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { @@ -70,7 +102,7 @@ static void PrintInfo(const std::vector& shapes, const std::ve printf(" floats: ["); for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) { - printf("%f", shapes[i].mesh.tags[t].floatValues[j]); + printf("%f", static_cast(shapes[i].mesh.tags[t].floatValues[j])); if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) { printf(", "); @@ -128,11 +160,12 @@ TestLoadObj( { std::cout << "Loading " << filename << std::endl; + tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; - bool ret = tinyobj::LoadObj(shapes, materials, err, filename, basepath, triangulate); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate); if (!err.empty()) { std::cerr << err << std::endl; @@ -143,7 +176,7 @@ TestLoadObj( return false; } - PrintInfo(shapes, materials, triangulate); + PrintInfo(attrib, shapes, materials, triangulate); return true; } @@ -223,13 +256,13 @@ std::string matStream( virtual ~MaterialStringStreamReader() {} virtual bool operator() ( const std::string& matId, - std::vector& materials, - std::map& matMap, - std::string& err) + std::vector* materials, + std::map* matMap, + std::string* err) { (void)matId; (void)err; - LoadMtl(matMap, materials, m_matSStream); + LoadMtl(matMap, materials, &m_matSStream); return true; } @@ -238,10 +271,11 @@ std::string matStream( }; MaterialStringStreamReader matSSReader(matStream); + tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; - bool ret = tinyobj::LoadObj(shapes, materials, err, objStream, matSSReader); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader); if (!err.empty()) { std::cerr << err << std::endl; @@ -251,7 +285,7 @@ std::string matStream( return false; } - PrintInfo(shapes, materials); + PrintInfo(attrib, shapes, materials); return true; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 8bdd0a32..b3a7589c 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1,10 +1,11 @@ // -// Copyright 2012-2015, Syoyo Fujita. +// Copyright 2012-2016, Syoyo Fujita. // // Licensed under 2-clause BSD license. // // +// version devel : Change data structure. Support different index for vertex/normal/texcoord(#73, #39) // version 0.9.20: Fixes creating per-face material using `usemtl`(#68) // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) // version 0.9.16: Make tinyobjloader header-only @@ -81,11 +82,16 @@ typedef struct { std::vector stringValues; } tag_t; +// Index struct to support differnt indices for vtx/normal/texcoord. +// -1 means not used. typedef struct { - std::vector positions; - std::vector normals; - std::vector texcoords; - std::vector indices; + int vertex_index; + int normal_index; + int texcoord_index; +} index_t; + +typedef struct { + std::vector indices; std::vector num_vertices; // The number of vertices per face. Up to 255. std::vector material_ids; // per-face material ID @@ -97,15 +103,21 @@ typedef struct { mesh_t mesh; } shape_t; +typedef struct { + std::vector positions; + std::vector normals; + std::vector texcoords; +} attrib_t; + class MaterialReader { public: MaterialReader() {} virtual ~MaterialReader(); virtual bool operator()(const std::string &matId, - std::vector &materials, - std::map &matMap, - std::string &err) = 0; + std::vector *materials, + std::map *matMap, + std::string *err) = 0; }; class MaterialFileReader : public MaterialReader { @@ -114,14 +126,15 @@ class MaterialFileReader : public MaterialReader { : m_mtlBasePath(mtl_basepath) {} virtual ~MaterialFileReader() {} virtual bool operator()(const std::string &matId, - std::vector &materials, - std::map &matMap, std::string &err); + std::vector *materials, + std::map *matMap, std::string *err); private: std::string m_mtlBasePath; }; /// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data /// 'shapes' will be filled with parsed shape data /// The function returns error string. /// Returns true when loading .obj become success. @@ -129,9 +142,10 @@ class MaterialFileReader : public MaterialReader { /// 'mtl_basepath' is optional, and used for base path for .mtl file. /// 'triangulate' is optional, and used whether triangulate polygon face in .obj /// or not. -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string &err, // [output] +bool LoadObj(attrib_t *attrib, + std::vector *shapes, + std::vector *materials, + std::string *err, const char *filename, const char *mtl_basepath = NULL, bool triangulate = true); @@ -139,16 +153,17 @@ bool LoadObj(std::vector &shapes, // [output] /// std::istream for materials. /// Returns true when loading .obj become success. /// Returns warning and error message into `err` -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string &err, // [output] - std::istream &inStream, MaterialReader &readMatFn, +bool LoadObj(attrib_t *attrib, + std::vector *shapes, + std::vector *materials, + std::string *err, + std::istream *inStream, MaterialReader *readMatFn, bool triangulate = true); /// Loads materials into std::map -void LoadMtl(std::map &material_map, // [output] - std::vector &materials, // [output] - std::istream &inStream); +void LoadMtl(std::map *material_map, + std::vector *materials, + std::istream *inStream); } #ifdef TINYOBJLOADER_IMPLEMENTATION @@ -204,7 +219,7 @@ struct obj_shape { }; #define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') ) -#define IS_DIGIT( x ) ( (unsigned int)( (x) - '0' ) < (unsigned int)10 ) +#define IS_DIGIT( x ) ( static_cast( (x) - '0' ) < static_cast(10) ) #define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') ) // Make index zero-base, and also support relative index. @@ -447,45 +462,6 @@ static vertex_index parseTriple(const char *&token, int vsize, int vnsize, return vi; } -static unsigned int -updateVertex(std::map &vertexCache, - std::vector &positions, std::vector &normals, - std::vector &texcoords, - const std::vector &in_positions, - const std::vector &in_normals, - const std::vector &in_texcoords, const vertex_index &i) { - const std::map::iterator it = vertexCache.find(i); - - if (it != vertexCache.end()) { - // found cache - return it->second; - } - - assert(in_positions.size() > static_cast(3 * i.v_idx + 2)); - - positions.push_back(in_positions[3 * static_cast(i.v_idx) + 0]); - positions.push_back(in_positions[3 * static_cast(i.v_idx) + 1]); - positions.push_back(in_positions[3 * static_cast(i.v_idx) + 2]); - - if ((i.vn_idx >= 0) && - (static_cast(i.vn_idx * 3 + 2) < in_normals.size())) { - normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 0]); - normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 1]); - normals.push_back(in_normals[3 * static_cast(i.vn_idx) + 2]); - } - - if ((i.vt_idx >= 0) && - (static_cast(i.vt_idx * 2 + 1) < in_texcoords.size())) { - texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 0]); - texcoords.push_back(in_texcoords[2 * static_cast(i.vt_idx) + 1]); - } - - unsigned int idx = static_cast(positions.size() / 3 - 1); - vertexCache[i] = idx; - - return idx; -} - static void InitMaterial(material_t &material) { material.name = ""; material.ambient_texname = ""; @@ -510,13 +486,13 @@ static void InitMaterial(material_t &material) { } static bool exportFaceGroupToShape( - shape_t &shape, std::map vertexCache, + shape_t &shape, const std::vector &in_positions, const std::vector &in_normals, const std::vector &in_texcoords, const std::vector > &faceGroup, std::vector &tags, const int material_id, const std::string &name, - bool clearCache, bool triangulate) { + bool triangulate) { if (faceGroup.empty()) { return false; } @@ -538,19 +514,20 @@ static bool exportFaceGroupToShape( i1 = i2; i2 = face[k]; - unsigned int v0 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0); - unsigned int v1 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1); - unsigned int v2 = updateVertex( - vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2); - - shape.mesh.indices.push_back(v0); - shape.mesh.indices.push_back(v1); - shape.mesh.indices.push_back(v2); + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape.mesh.indices.push_back(idx0); + shape.mesh.indices.push_back(idx1); + shape.mesh.indices.push_back(idx2); shape.mesh.num_vertices.push_back(3); shape.mesh.material_ids.push_back(material_id); @@ -558,12 +535,10 @@ static bool exportFaceGroupToShape( } else { for (size_t k = 0; k < npolys; k++) { - unsigned int v = - updateVertex(vertexCache, shape.mesh.positions, shape.mesh.normals, - shape.mesh.texcoords, in_positions, in_normals, - in_texcoords, face[k]); - - shape.mesh.indices.push_back(v); + index_t idx; + idx.vertex_index = face[k].v_idx; + idx.normal_index = face[k].vn_idx; + idx.texcoord_index = face[k].vt_idx; } shape.mesh.num_vertices.push_back(static_cast(npolys)); @@ -574,14 +549,11 @@ static bool exportFaceGroupToShape( shape.name = name; shape.mesh.tags.swap(tags); - if (clearCache) - vertexCache.clear(); - return true; } -void LoadMtl(std::map &material_map, - std::vector &materials, std::istream &inStream) { +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream) { // Create a default material anyway. material_t material; @@ -589,8 +561,8 @@ void LoadMtl(std::map &material_map, size_t maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. - while (inStream.peek() != -1) { - inStream.getline(&buf[0], static_cast(maxchars)); + while (inStream->peek() != -1) { + inStream->getline(&buf[0], static_cast(maxchars)); std::string linebuf(&buf[0]); @@ -624,9 +596,9 @@ void LoadMtl(std::map &material_map, if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { // flush previous material. if (!material.name.empty()) { - material_map.insert(std::pair( - material.name, static_cast(materials.size()))); - materials.push_back(material); + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); } // initial temporary material @@ -803,15 +775,15 @@ void LoadMtl(std::map &material_map, } } // flush last material. - material_map.insert(std::pair( - material.name, static_cast(materials.size()))); - materials.push_back(material); + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); } bool MaterialFileReader::operator()(const std::string &matId, - std::vector &materials, - std::map &matMap, - std::string &err) { + std::vector *materials, + std::map *matMap, + std::string *err) { std::string filepath; if (!m_mtlBasePath.empty()) { @@ -821,29 +793,37 @@ bool MaterialFileReader::operator()(const std::string &matId, } std::ifstream matIStream(filepath.c_str()); - LoadMtl(matMap, materials, matIStream); + LoadMtl(matMap, materials, &matIStream); if (!matIStream) { std::stringstream ss; ss << "WARN: Material file [ " << filepath << " ] not found. Created a default material."; - err += ss.str(); + if (err) { + (*err) += ss.str(); + } } return true; } -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string &err, const char *filename, const char *mtl_basepath, +bool LoadObj(attrib_t *attrib, + std::vector *shapes, + std::vector *materials, + std::string *err, const char *filename, const char *mtl_basepath, bool trianglulate) { - shapes.clear(); + attrib->positions.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + shapes->clear(); std::stringstream errss; std::ifstream ifs(filename); if (!ifs) { errss << "Cannot open file [" << filename << "]" << std::endl; - err = errss.str(); + if (err) { + (*err) = errss.str(); + } return false; } @@ -853,13 +833,14 @@ bool LoadObj(std::vector &shapes, // [output] } MaterialFileReader matFileReader(basePath); - return LoadObj(shapes, materials, err, ifs, matFileReader, trianglulate); + return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, trianglulate); } -bool LoadObj(std::vector &shapes, // [output] - std::vector &materials, // [output] - std::string &err, std::istream &inStream, - MaterialReader &readMatFn, bool triangulate) { +bool LoadObj(attrib_t *attrib, + std::vector *shapes, + std::vector *materials, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn, bool triangulate) { std::stringstream errss; std::vector v; @@ -871,15 +852,15 @@ bool LoadObj(std::vector &shapes, // [output] // material std::map material_map; - std::map vertexCache; + //std::map vertexCache; int material = -1; shape_t shape; int maxchars = 8192; // Alloc enough size. std::vector buf(static_cast(maxchars)); // Alloc enough size. - while (inStream.peek() != -1) { - inStream.getline(&buf[0], maxchars); + while (inStream->peek() != -1) { + inStream->getline(&buf[0], maxchars); std::string linebuf(&buf[0]); @@ -985,8 +966,8 @@ bool LoadObj(std::vector &shapes, // [output] if (newMaterialId != material) { // Create per-face material - exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, - material, name, true, triangulate); + exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, + material, name, triangulate); faceGroup.clear(); material = newMaterialId; } @@ -1005,8 +986,10 @@ bool LoadObj(std::vector &shapes, // [output] #endif std::string err_mtl; - bool ok = readMatFn(namebuf, materials, material_map, err_mtl); - err += err_mtl; + bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl); + if (err) { + (*err) += err_mtl; + } if (!ok) { faceGroup.clear(); // for safety @@ -1021,10 +1004,10 @@ bool LoadObj(std::vector &shapes, // [output] // flush previous face group. bool ret = - exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, - material, name, true, triangulate); + exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, + material, name, triangulate); if (ret) { - shapes.push_back(shape); + shapes->push_back(shape); } shape = shape_t(); @@ -1058,10 +1041,10 @@ bool LoadObj(std::vector &shapes, // [output] // flush previous face group. bool ret = - exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, tags, - material, name, true, triangulate); + exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, + material, name, triangulate); if (ret) { - shapes.push_back(shape); + shapes->push_back(shape); } // material = -1; @@ -1129,14 +1112,21 @@ bool LoadObj(std::vector &shapes, // [output] // Ignore unknown command. } - bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup, - tags, material, name, true, triangulate); + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, + tags, material, name, triangulate); if (ret) { - shapes.push_back(shape); + shapes->push_back(shape); } faceGroup.clear(); // for safety - err += errss.str(); + if (err) { + (*err) += errss.str(); + } + + attrib->positions.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + return true; } From 54bd46014c20790d8c8cd19bb94cce2904cfb28a Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 16 Apr 2016 20:25:33 +0900 Subject: [PATCH 106/585] Use google C++ code style. --- .clang-format | 2 +- test.cc | 28 ++-- tiny_obj_loader.h | 391 +++++++++++++++++++++------------------------- 3 files changed, 197 insertions(+), 224 deletions(-) diff --git a/.clang-format b/.clang-format index a94a2c44..74210b03 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ --- -BasedOnStyle: LLVM +BasedOnStyle: Google IndentWidth: 2 TabWidth: 2 UseTab: Never diff --git a/test.cc b/test.cc index 88d8be18..72b4fa21 100644 --- a/test.cc +++ b/test.cc @@ -10,18 +10,18 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials, bool triangulate = true) { - std::cout << "# of positions : " << (attrib.positions.size() / 3) << std::endl; + std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl; - for (size_t v = 0; v < attrib.positions.size() / 3; v++) { + for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { printf(" v[%ld] = (%f, %f, %f)\n", v, - static_cast(attrib.positions[3*v+0]), - static_cast(attrib.positions[3*v+1]), - static_cast(attrib.positions[3*v+2])); + static_cast(attrib.vertices[3*v+0]), + static_cast(attrib.vertices[3*v+1]), + static_cast(attrib.vertices[3*v+2])); } for (size_t v = 0; v < attrib.normals.size() / 3; v++) { @@ -126,15 +126,15 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(materials[i].ambient[0]), static_cast(materials[i].ambient[1]), static_cast(materials[i].ambient[2])); + printf(" material.Kd = (%f, %f ,%f)\n", static_cast(materials[i].diffuse[0]), static_cast(materials[i].diffuse[1]), static_cast(materials[i].diffuse[2])); + printf(" material.Ks = (%f, %f ,%f)\n", static_cast(materials[i].specular[0]), static_cast(materials[i].specular[1]), static_cast(materials[i].specular[2])); + printf(" material.Tr = (%f, %f ,%f)\n", static_cast(materials[i].transmittance[0]), static_cast(materials[i].transmittance[1]), static_cast(materials[i].transmittance[2])); + printf(" material.Ke = (%f, %f ,%f)\n", static_cast(materials[i].emission[0]), static_cast(materials[i].emission[1]), static_cast(materials[i].emission[2])); + printf(" material.Ns = %f\n", static_cast(materials[i].shininess)); + printf(" material.Ni = %f\n", static_cast(materials[i].ior)); + printf(" material.dissolve = %f\n", static_cast(materials[i].dissolve)); + printf(" material.illum = %d\n", materials[i].illum); printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index b3a7589c..e67bed32 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -5,7 +5,8 @@ // // -// version devel : Change data structure. Support different index for vertex/normal/texcoord(#73, #39) +// version devel : Change data structure. Support different index for +// vertex/normal/texcoord(#73, #39) // version 0.9.20: Fixes creating per-face material using `usemtl`(#68) // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) // version 0.9.16: Make tinyobjloader header-only @@ -57,20 +58,20 @@ typedef struct { float transmittance[3]; float emission[3]; float shininess; - float ior; // index of refraction - float dissolve; // 1 == opaque; 0 == fully transparent + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent // illumination model (see http://www.fileformat.info/format/material/) int illum; - int dummy; // Suppress padding warning. + int dummy; // Suppress padding warning. - std::string ambient_texname; // map_Ka - std::string diffuse_texname; // map_Kd - std::string specular_texname; // map_Ks - std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, bump - std::string displacement_texname; // disp - std::string alpha_texname; // map_d + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d std::map unknown_parameter; } material_t; @@ -93,9 +94,9 @@ typedef struct { typedef struct { std::vector indices; std::vector - num_vertices; // The number of vertices per face. Up to 255. - std::vector material_ids; // per-face material ID - std::vector tags; // SubD tag + num_vertices; // The number of vertices per face. Up to 255. + std::vector material_ids; // per-face material ID + std::vector tags; // SubD tag } mesh_t; typedef struct { @@ -103,14 +104,15 @@ typedef struct { mesh_t mesh; } shape_t; +// Vertex attributes typedef struct { - std::vector positions; - std::vector normals; - std::vector texcoords; + std::vector vertices; // 'v' + std::vector normals; // 'vn' + std::vector texcoords; // 'vt' } attrib_t; class MaterialReader { -public: + public: MaterialReader() {} virtual ~MaterialReader(); @@ -121,15 +123,15 @@ class MaterialReader { }; class MaterialFileReader : public MaterialReader { -public: - MaterialFileReader(const std::string &mtl_basepath) + public: + explicit MaterialFileReader(const std::string &mtl_basepath) : m_mtlBasePath(mtl_basepath) {} virtual ~MaterialFileReader() {} virtual bool operator()(const std::string &matId, std::vector *materials, std::map *matMap, std::string *err); -private: + private: std::string m_mtlBasePath; }; @@ -142,10 +144,8 @@ class MaterialFileReader : public MaterialReader { /// 'mtl_basepath' is optional, and used for base path for .mtl file. /// 'triangulate' is optional, and used whether triangulate polygon face in .obj /// or not. -bool LoadObj(attrib_t *attrib, - std::vector *shapes, - std::vector *materials, - std::string *err, +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, const char *filename, const char *mtl_basepath = NULL, bool triangulate = true); @@ -153,18 +153,16 @@ bool LoadObj(attrib_t *attrib, /// std::istream for materials. /// Returns true when loading .obj become success. /// Returns warning and error message into `err` -bool LoadObj(attrib_t *attrib, - std::vector *shapes, - std::vector *materials, - std::string *err, +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, std::istream *inStream, MaterialReader *readMatFn, bool triangulate = true); /// Loads materials into std::map void LoadMtl(std::map *material_map, - std::vector *materials, - std::istream *inStream); -} + std::vector *materials, std::istream *inStream); + +} // namespace tinyobj #ifdef TINYOBJLOADER_IMPLEMENTATION #include @@ -173,12 +171,11 @@ void LoadMtl(std::map *material_map, #include #include #include +#include #include #include -#include "tiny_obj_loader.h" - namespace tinyobj { MaterialReader::~MaterialReader() {} @@ -202,12 +199,9 @@ struct tag_sizes { // for std::map static inline bool operator<(const vertex_index &a, const vertex_index &b) { - if (a.v_idx != b.v_idx) - return (a.v_idx < b.v_idx); - if (a.vn_idx != b.vn_idx) - return (a.vn_idx < b.vn_idx); - if (a.vt_idx != b.vt_idx) - return (a.vt_idx < b.vt_idx); + if (a.v_idx != b.v_idx) return (a.v_idx < b.v_idx); + if (a.vn_idx != b.vn_idx) return (a.vn_idx < b.vn_idx); + if (a.vt_idx != b.vt_idx) return (a.vt_idx < b.vt_idx); return false; } @@ -218,32 +212,31 @@ struct obj_shape { std::vector vt; }; -#define IS_SPACE( x ) ( ( (x) == ' ') || ( (x) == '\t') ) -#define IS_DIGIT( x ) ( static_cast( (x) - '0' ) < static_cast(10) ) -#define IS_NEW_LINE( x ) ( ( (x) == '\r') || ( (x) == '\n') || ( (x) == '\0') ) +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) // Make index zero-base, and also support relative index. static inline int fixIndex(int idx, int n) { - if (idx > 0) - return idx - 1; - if (idx == 0) - return 0; - return n + idx; // negative value = relative + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative } -static inline std::string parseString(const char *&token) { +static inline std::string parseString(const char **token) { std::string s; - token += strspn(token, " \t"); - size_t e = strcspn(token, " \t\r"); - s = std::string(token, &token[e]); - token += e; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; return s; } -static inline int parseInt(const char *&token) { - token += strspn(token, " \t"); - int i = atoi(token); - token += strcspn(token, " \t\r"); +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); return i; } @@ -261,7 +254,7 @@ static inline int parseInt(const char *&token) { // float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; // // Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 // // If the parsing is a success, result is set to the parsed value and true // is returned. @@ -322,11 +315,9 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { } // We must make sure we actually got something. - if (read == 0) - goto fail; + if (read == 0) goto fail; // We allow numbers of form "#", "###" etc. - if (!end_not_reached) - goto assemble; + if (!end_not_reached) goto assemble; // Read the decimal part. if (*curr == '.') { @@ -343,8 +334,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { goto assemble; } - if (!end_not_reached) - goto assemble; + if (!end_not_reached) goto assemble; // Read the exponent part. if (*curr == 'e' || *curr == 'E') { @@ -367,8 +357,7 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { read++; } exponent *= (exp_sign == '+' ? 1 : -1); - if (read == 0) - goto fail; + if (read == 0) goto fail; } assemble: @@ -378,121 +367,118 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { fail: return false; } -static inline float parseFloat(const char *&token) { - token += strspn(token, " \t"); + +static inline float parseFloat(const char **token) { + (*token) += strspn((*token), " \t"); #ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER - float f = (float)atof(token); - token += strcspn(token, " \t\r"); + float f = static_cast(atof(*token)); + (*token) += strcspn((*token), " \t\r"); #else - const char *end = token + strcspn(token, " \t\r"); + const char *end = (*token) + strcspn((*token), " \t\r"); double val = 0.0; - tryParseDouble(token, end, &val); + tryParseDouble((*token), end, &val); float f = static_cast(val); - token = end; + (*token) = end; #endif return f; } -static inline void parseFloat2(float &x, float &y, const char *&token) { - x = parseFloat(token); - y = parseFloat(token); +static inline void parseFloat2(float *x, float *y, const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); } -static inline void parseFloat3(float &x, float &y, float &z, - const char *&token) { - x = parseFloat(token); - y = parseFloat(token); - z = parseFloat(token); +static inline void parseFloat3(float *x, float *y, float *z, + const char **token) { + (*x) = parseFloat(token); + (*y) = parseFloat(token); + (*z) = parseFloat(token); } -static tag_sizes parseTagTriple(const char *&token) { +static tag_sizes parseTagTriple(const char **token) { tag_sizes ts; - ts.num_ints = atoi(token); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { return ts; } - token++; + (*token)++; - ts.num_floats = atoi(token); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { + ts.num_floats = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { return ts; } - token++; + (*token)++; - ts.num_strings = atoi(token); - token += strcspn(token, "/ \t\r") + 1; + ts.num_strings = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r") + 1; return ts; } // Parse triples: i, i/j/k, i//k, i/j -static vertex_index parseTriple(const char *&token, int vsize, int vnsize, +static vertex_index parseTriple(const char **token, int vsize, int vnsize, int vtsize) { vertex_index vi(-1); - vi.v_idx = fixIndex(atoi(token), vsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { + vi.v_idx = fixIndex(atoi((*token)), vsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { return vi; } - token++; + (*token)++; // i//k - if (token[0] == '/') { - token++; - vi.vn_idx = fixIndex(atoi(token), vnsize); - token += strcspn(token, "/ \t\r"); + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); return vi; } // i/j/k or i/j - vi.vt_idx = fixIndex(atoi(token), vtsize); - token += strcspn(token, "/ \t\r"); - if (token[0] != '/') { + vi.vt_idx = fixIndex(atoi((*token)), vtsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { return vi; } // i/j/k - token++; // skip '/' - vi.vn_idx = fixIndex(atoi(token), vnsize); - token += strcspn(token, "/ \t\r"); + (*token)++; // skip '/' + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); return vi; } -static void InitMaterial(material_t &material) { - material.name = ""; - material.ambient_texname = ""; - material.diffuse_texname = ""; - material.specular_texname = ""; - material.specular_highlight_texname = ""; - material.bump_texname = ""; - material.displacement_texname = ""; - material.alpha_texname = ""; +static void InitMaterial(material_t *material) { + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->alpha_texname = ""; for (int i = 0; i < 3; i++) { - material.ambient[i] = 0.f; - material.diffuse[i] = 0.f; - material.specular[i] = 0.f; - material.transmittance[i] = 0.f; - material.emission[i] = 0.f; + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; } - material.illum = 0; - material.dissolve = 1.f; - material.shininess = 1.f; - material.ior = 1.f; - material.unknown_parameter.clear(); + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; + material->unknown_parameter.clear(); } static bool exportFaceGroupToShape( - shape_t &shape, - const std::vector &in_positions, - const std::vector &in_normals, - const std::vector &in_texcoords, - const std::vector > &faceGroup, - std::vector &tags, const int material_id, const std::string &name, - bool triangulate) { + shape_t *shape, const std::vector > &faceGroup, + const std::vector &tags, const int material_id, + const std::string &name, bool triangulate) { if (faceGroup.empty()) { return false; } @@ -508,7 +494,6 @@ static bool exportFaceGroupToShape( size_t npolys = face.size(); if (triangulate) { - // Polygon -> triangle fan conversion for (size_t k = 2; k < npolys; k++) { i1 = i2; @@ -525,15 +510,14 @@ static bool exportFaceGroupToShape( idx2.normal_index = i2.vn_idx; idx2.texcoord_index = i2.vt_idx; - shape.mesh.indices.push_back(idx0); - shape.mesh.indices.push_back(idx1); - shape.mesh.indices.push_back(idx2); + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); - shape.mesh.num_vertices.push_back(3); - shape.mesh.material_ids.push_back(material_id); + shape->mesh.num_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); } } else { - for (size_t k = 0; k < npolys; k++) { index_t idx; idx.vertex_index = face[k].v_idx; @@ -541,26 +525,25 @@ static bool exportFaceGroupToShape( idx.texcoord_index = face[k].vt_idx; } - shape.mesh.num_vertices.push_back(static_cast(npolys)); - shape.mesh.material_ids.push_back(material_id); // per face + shape->mesh.num_vertices.push_back(static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face } } - shape.name = name; - shape.mesh.tags.swap(tags); + shape->name = name; + shape->mesh.tags = tags; return true; } void LoadMtl(std::map *material_map, std::vector *materials, std::istream *inStream) { - // Create a default material anyway. material_t material; - InitMaterial(material); + InitMaterial(&material); - size_t maxchars = 8192; // Alloc enough size. - std::vector buf(maxchars); // Alloc enough size. + size_t maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. while (inStream->peek() != -1) { inStream->getline(&buf[0], static_cast(maxchars)); @@ -586,11 +569,9 @@ void LoadMtl(std::map *material_map, token += strspn(token, " \t"); assert(token); - if (token[0] == '\0') - continue; // empty line + if (token[0] == '\0') continue; // empty line - if (token[0] == '#') - continue; // comment line + if (token[0] == '#') continue; // comment line // new mtl if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { @@ -602,7 +583,7 @@ void LoadMtl(std::map *material_map, } // initial temporary material - InitMaterial(material); + InitMaterial(&material); // set new mtl name char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; @@ -620,7 +601,7 @@ void LoadMtl(std::map *material_map, if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { token += 2; float r, g, b; - parseFloat3(r, g, b, token); + parseFloat3(&r, &g, &b, &token); material.ambient[0] = r; material.ambient[1] = g; material.ambient[2] = b; @@ -631,7 +612,7 @@ void LoadMtl(std::map *material_map, if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { token += 2; float r, g, b; - parseFloat3(r, g, b, token); + parseFloat3(&r, &g, &b, &token); material.diffuse[0] = r; material.diffuse[1] = g; material.diffuse[2] = b; @@ -642,7 +623,7 @@ void LoadMtl(std::map *material_map, if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { token += 2; float r, g, b; - parseFloat3(r, g, b, token); + parseFloat3(&r, &g, &b, &token); material.specular[0] = r; material.specular[1] = g; material.specular[2] = b; @@ -653,7 +634,7 @@ void LoadMtl(std::map *material_map, if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { token += 2; float r, g, b; - parseFloat3(r, g, b, token); + parseFloat3(&r, &g, &b, &token); material.transmittance[0] = r; material.transmittance[1] = g; material.transmittance[2] = b; @@ -663,7 +644,7 @@ void LoadMtl(std::map *material_map, // ior(index of refraction) if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { token += 2; - material.ior = parseFloat(token); + material.ior = parseFloat(&token); continue; } @@ -671,7 +652,7 @@ void LoadMtl(std::map *material_map, if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { token += 2; float r, g, b; - parseFloat3(r, g, b, token); + parseFloat3(&r, &g, &b, &token); material.emission[0] = r; material.emission[1] = g; material.emission[2] = b; @@ -681,27 +662,27 @@ void LoadMtl(std::map *material_map, // shininess if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { token += 2; - material.shininess = parseFloat(token); + material.shininess = parseFloat(&token); continue; } // illum model if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { token += 6; - material.illum = parseInt(token); + material.illum = parseInt(&token); continue; } // dissolve if ((token[0] == 'd' && IS_SPACE(token[1]))) { token += 1; - material.dissolve = parseFloat(token); + material.dissolve = parseFloat(&token); continue; } if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { token += 2; // Invert value of Tr(assume Tr is in range [0, 1]) - material.dissolve = 1.0f - parseFloat(token); + material.dissolve = 1.0f - parseFloat(&token); continue; } @@ -805,13 +786,11 @@ bool MaterialFileReader::operator()(const std::string &matId, return true; } -bool LoadObj(attrib_t *attrib, - std::vector *shapes, - std::vector *materials, - std::string *err, const char *filename, const char *mtl_basepath, +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + const char *filename, const char *mtl_basepath, bool trianglulate) { - - attrib->positions.clear(); + attrib->vertices.clear(); attrib->normals.clear(); attrib->texcoords.clear(); shapes->clear(); @@ -833,14 +812,14 @@ bool LoadObj(attrib_t *attrib, } MaterialFileReader matFileReader(basePath); - return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, trianglulate); + return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, + trianglulate); } -bool LoadObj(attrib_t *attrib, - std::vector *shapes, - std::vector *materials, - std::string *err, std::istream *inStream, - MaterialReader *readMatFn, bool triangulate) { +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + std::istream *inStream, MaterialReader *readMatFn, + bool triangulate) { std::stringstream errss; std::vector v; @@ -852,13 +831,13 @@ bool LoadObj(attrib_t *attrib, // material std::map material_map; - //std::map vertexCache; + // std::map vertexCache; int material = -1; shape_t shape; - int maxchars = 8192; // Alloc enough size. - std::vector buf(static_cast(maxchars)); // Alloc enough size. + int maxchars = 8192; // Alloc enough size. + std::vector buf(static_cast(maxchars)); // Alloc enough size. while (inStream->peek() != -1) { inStream->getline(&buf[0], maxchars); @@ -884,17 +863,15 @@ bool LoadObj(attrib_t *attrib, token += strspn(token, " \t"); assert(token); - if (token[0] == '\0') - continue; // empty line + if (token[0] == '\0') continue; // empty line - if (token[0] == '#') - continue; // comment line + if (token[0] == '#') continue; // comment line // vertex if (token[0] == 'v' && IS_SPACE((token[1]))) { token += 2; float x, y, z; - parseFloat3(x, y, z, token); + parseFloat3(&x, &y, &z, &token); v.push_back(x); v.push_back(y); v.push_back(z); @@ -905,7 +882,7 @@ bool LoadObj(attrib_t *attrib, if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { token += 3; float x, y, z; - parseFloat3(x, y, z, token); + parseFloat3(&x, &y, &z, &token); vn.push_back(x); vn.push_back(y); vn.push_back(z); @@ -916,7 +893,7 @@ bool LoadObj(attrib_t *attrib, if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { token += 3; float x, y; - parseFloat2(x, y, token); + parseFloat2(&x, &y, &token); vt.push_back(x); vt.push_back(y); continue; @@ -931,7 +908,7 @@ bool LoadObj(attrib_t *attrib, face.reserve(3); while (!IS_NEW_LINE(token[0])) { - vertex_index vi = parseTriple(token, static_cast(v.size() / 3), + vertex_index vi = parseTriple(&token, static_cast(v.size() / 3), static_cast(vn.size() / 3), static_cast(vt.size() / 2)); face.push_back(vi); @@ -948,7 +925,6 @@ bool LoadObj(attrib_t *attrib, // use mtl if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; token += 7; #ifdef _MSC_VER @@ -966,8 +942,8 @@ bool LoadObj(attrib_t *attrib, if (newMaterialId != material) { // Create per-face material - exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, - material, name, triangulate); + exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); faceGroup.clear(); material = newMaterialId; } @@ -992,7 +968,7 @@ bool LoadObj(attrib_t *attrib, } if (!ok) { - faceGroup.clear(); // for safety + faceGroup.clear(); // for safety return false; } @@ -1001,11 +977,9 @@ bool LoadObj(attrib_t *attrib, // group name if (token[0] == 'g' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = - exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, - material, name, triangulate); + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); if (ret) { shapes->push_back(shape); } @@ -1019,9 +993,9 @@ bool LoadObj(attrib_t *attrib, names.reserve(2); while (!IS_NEW_LINE(token[0])) { - std::string str = parseString(token); + std::string str = parseString(&token); names.push_back(str); - token += strspn(token, " \t\r"); // skip tag + token += strspn(token, " \t\r"); // skip tag } assert(names.size() > 0); @@ -1038,11 +1012,9 @@ bool LoadObj(attrib_t *attrib, // object name if (token[0] == 'o' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = - exportFaceGroupToShape(shape, v, vn, vt, faceGroup, tags, - material, name, triangulate); + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); if (ret) { shapes->push_back(shape); } @@ -1078,7 +1050,7 @@ bool LoadObj(attrib_t *attrib, token += tag.name.size() + 1; - tag_sizes ts = parseTagTriple(token); + tag_sizes ts = parseTagTriple(&token); tag.intValues.resize(static_cast(ts.num_ints)); @@ -1089,7 +1061,7 @@ bool LoadObj(attrib_t *attrib, tag.floatValues.resize(static_cast(ts.num_floats)); for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { - tag.floatValues[i] = parseFloat(token); + tag.floatValues[i] = parseFloat(&token); token += strcspn(token, "/ \t\r") + 1; } @@ -1098,7 +1070,8 @@ bool LoadObj(attrib_t *attrib, char stringValueBuffer[4096]; #ifdef _MSC_VER - sscanf_s(token, "%s", stringValueBuffer, (unsigned)_countof(stringValueBuffer)); + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); #else sscanf(token, "%s", stringValueBuffer); #endif @@ -1112,26 +1085,26 @@ bool LoadObj(attrib_t *attrib, // Ignore unknown command. } - bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, - tags, material, name, triangulate); + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); if (ret) { shapes->push_back(shape); } - faceGroup.clear(); // for safety + faceGroup.clear(); // for safety if (err) { (*err) += errss.str(); } - attrib->positions.swap(v); + attrib->vertices.swap(v); attrib->normals.swap(vn); attrib->texcoords.swap(vt); return true; } -} // namespace +} // namespace tinyobj #endif -#endif // TINY_OBJ_LOADER_H_ +#endif // TINY_OBJ_LOADER_H_ From 72ef6cbb76bea690288f20e9d6fa61241ee3e0be Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 16:03:24 +0900 Subject: [PATCH 107/585] Add initial unit test codes using Catch. Add Kuroga build script. --- build.ninja | 8 +- deps/cpplint.py | 6323 ++++++++++++ test.cc => loader_example.cc | 4 +- cornell_box.mtl => models/cornell_box.mtl | 0 cornell_box.obj => models/cornell_box.obj | 0 premake4.lua | 6 +- tests/Makefile | 13 + tests/README.md | 25 + tests/catch.hpp | 10445 ++++++++++++++++++++ tests/config-msvc.py | 52 + tests/config-posix.py | 53 + tests/kuroga.py | 312 + tests/tester.cc | 324 + tests/vcbuild.bat | 3 + tiny_obj_loader.h | 340 +- 15 files changed, 17897 insertions(+), 11 deletions(-) create mode 100755 deps/cpplint.py rename test.cc => loader_example.cc (98%) rename cornell_box.mtl => models/cornell_box.mtl (100%) rename cornell_box.obj => models/cornell_box.obj (100%) create mode 100644 tests/Makefile create mode 100644 tests/README.md create mode 100644 tests/catch.hpp create mode 100644 tests/config-msvc.py create mode 100644 tests/config-posix.py create mode 100755 tests/kuroga.py create mode 100644 tests/tester.cc create mode 100644 tests/vcbuild.bat diff --git a/build.ninja b/build.ninja index 3a26d974..5048ff1d 100644 --- a/build.ninja +++ b/build.ninja @@ -3,6 +3,8 @@ cc = clang cxx = clang++ cflags = -Werror -Weverything cxxflags = -Werror -Weverything +#cflags = -O2 +#cxxflags = -O2 rule compile command = $cxx $cxxflags -c $in -o $out @@ -10,7 +12,7 @@ rule compile rule link command = $cxx $in -o $out -build test.o: compile test.cc -build test: link test.o +build loader_example.o: compile loader_example.cc +build loader_example: link loader_example.o -default test +default loader_example diff --git a/deps/cpplint.py b/deps/cpplint.py new file mode 100755 index 00000000..ccc25d4c --- /dev/null +++ b/deps/cpplint.py @@ -0,0 +1,6323 @@ +#!/usr/bin/env python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] [--root=subdir] + [--linelength=digits] + [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the + extensions with the --extensions flag. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuming that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ + + linelength=digits + This is the allowed line length for the project. The default value is + 80 characters. + + Examples: + --linelength=120 + + extensions=extension,extension,... + The allowed file extensions that cpplint will check + + Examples: + --extensions=hpp,cpp + + cpplint.py supports per-directory configurations specified in CPPLINT.cfg + files. CPPLINT.cfg file can contain a number of key=value pairs. + Currently the following options are supported: + + set noparent + filter=+filter1,-filter2,... + exclude_files=regex + linelength=80 + + "set noparent" option prevents cpplint from traversing directory tree + upwards looking for more .cfg files in parent directories. This option + is usually placed in the top-level project directory. + + The "filter" option is similar in function to --filter flag. It specifies + message filters in addition to the |_DEFAULT_FILTERS| and those specified + through --filter command-line flag. + + "exclude_files" allows to specify a regular expression to be matched against + a file name. If the expression matches, the file is skipped and not run + through liner. + + "linelength" allows to specify the allowed line length for the project. + + CPPLINT.cfg has an effect on files in the same directory and all + sub-directories, unless overridden by a nested configuration file. + + Example file: + filter=-build/include_order,+build/include_alpha + exclude_files=.*\.cc + + The above example disables build/include_order warning and enables + build/include_alpha as well as excludes all .cc from being + processed by linter, in the current directory (where the .cfg + file is located) and all sub-directories. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +_ERROR_CATEGORIES = [ + 'build/class', + 'build/c++11', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/inheritance', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/nolint', + 'readability/nul', + 'readability/strings', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/indentation_namespace', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/vlog', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_conditional_body', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + ] + +# These error categories are no longer enforced by cpplint, but for backwards- +# compatibility they may still appear in NOLINT comments. +_LEGACY_ERROR_CATEGORIES = [ + 'readability/streams', + ] + +# The default state of the category filter. This is overridden by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# C++ headers +_CPP_HEADERS = frozenset([ + # Legacy + 'algobase.h', + 'algo.h', + 'alloc.h', + 'builtinbuf.h', + 'bvector.h', + 'complex.h', + 'defalloc.h', + 'deque.h', + 'editbuf.h', + 'fstream.h', + 'function.h', + 'hash_map', + 'hash_map.h', + 'hash_set', + 'hash_set.h', + 'hashtable.h', + 'heap.h', + 'indstream.h', + 'iomanip.h', + 'iostream.h', + 'istream.h', + 'iterator.h', + 'list.h', + 'map.h', + 'multimap.h', + 'multiset.h', + 'ostream.h', + 'pair.h', + 'parsestream.h', + 'pfstream.h', + 'procbuf.h', + 'pthread_alloc', + 'pthread_alloc.h', + 'rope', + 'rope.h', + 'ropeimpl.h', + 'set.h', + 'slist', + 'slist.h', + 'stack.h', + 'stdiostream.h', + 'stl_alloc.h', + 'stl_relops.h', + 'streambuf.h', + 'stream.h', + 'strfile.h', + 'strstream.h', + 'tempbuf.h', + 'tree.h', + 'type_traits.h', + 'vector.h', + # 17.6.1.2 C++ library headers + 'algorithm', + 'array', + 'atomic', + 'bitset', + 'chrono', + 'codecvt', + 'complex', + 'condition_variable', + 'deque', + 'exception', + 'forward_list', + 'fstream', + 'functional', + 'future', + 'initializer_list', + 'iomanip', + 'ios', + 'iosfwd', + 'iostream', + 'istream', + 'iterator', + 'limits', + 'list', + 'locale', + 'map', + 'memory', + 'mutex', + 'new', + 'numeric', + 'ostream', + 'queue', + 'random', + 'ratio', + 'regex', + 'set', + 'sstream', + 'stack', + 'stdexcept', + 'streambuf', + 'string', + 'strstream', + 'system_error', + 'thread', + 'tuple', + 'typeindex', + 'typeinfo', + 'type_traits', + 'unordered_map', + 'unordered_set', + 'utility', + 'valarray', + 'vector', + # 17.6.1.2 C++ headers for C library facilities + 'cassert', + 'ccomplex', + 'cctype', + 'cerrno', + 'cfenv', + 'cfloat', + 'cinttypes', + 'ciso646', + 'climits', + 'clocale', + 'cmath', + 'csetjmp', + 'csignal', + 'cstdalign', + 'cstdarg', + 'cstdbool', + 'cstddef', + 'cstdint', + 'cstdio', + 'cstdlib', + 'cstring', + 'ctgmath', + 'ctime', + 'cuchar', + 'cwchar', + 'cwctype', + ]) + + +# These headers are excluded from [build/include] and [build/include_order] +# checks: +# - Anything not following google file name conventions (containing an +# uppercase character, such as Python.h or nsStringAPI.h, for example). +# - Lua headers. +_THIRD_PARTY_HEADERS_PATTERN = re.compile( + r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments and multi-line strings +# but those have always been troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + + +_regexp_compile_cache = {} + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +# The allowed line length of files. +# This is set by --linelength flag. +_line_length = 80 + +# The allowed extensions for file names +# This is set by --extensions flag. +_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) + if matched: + if matched.group(1): + suppressed_line = linenum + 1 + else: + suppressed_line = linenum + category = matched.group(2) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(suppressed_line) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(suppressed_line) + elif category not in _LEGACY_ERROR_CATEGORIES: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + """Resets the set of NOLINT suppressions to empty.""" + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def ReplaceAll(pattern, rep, s): + """Replaces instances of pattern in a string with a replacement. + + The compiled regex is kept in a cache shared by Match and Search. + + Args: + pattern: regex pattern + rep: replacement text + s: search string + + Returns: + string with replacements made (or original string if no replacements) + """ + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].sub(rep, s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(object): + """Tracks line numbers for includes, and the order in which includes appear. + + include_list contains list of lists of (header, line number) pairs. + It's a lists of lists rather than just one flat list to make it + easier to update across preprocessor boundaries. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + self.include_list = [[]] + self.ResetSection('') + + def FindHeader(self, header): + """Check if a header has already been included. + + Args: + header: header to check. + Returns: + Line number of previous occurrence, or -1 if the header has not + been seen before. + """ + for section_list in self.include_list: + for f in section_list: + if f[0] == header: + return f[1] + return -1 + + def ResetSection(self, directive): + """Reset section checking for preprocessor directive. + + Args: + directive: preprocessor directive (e.g. "if", "else"). + """ + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + # Update list of includes. Note that we never pop from the + # include list. + if directive in ('if', 'ifdef', 'ifndef'): + self.include_list.append([]) + elif directive in ('else', 'elif'): + self.include_list[-1] = [] + + def SetLastHeader(self, header_path): + self._last_header = header_path + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + header_path: Canonicalized header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + # If previous section is different from current section, _last_header will + # be reset to empty string, so it's always less than current header. + # + # If previous line was a blank line, assume that the headers are + # intentionally sorted the way they are. + if (self._last_header > header_path and + Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): + return False + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + # backup of filter list. Used to restore the state after each file. + self._filters_backup = self.filters[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + self.AddFilters(filters) + + def AddFilters(self, filters): + """ Adds more filters to the existing list of error-message filters. """ + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def BackupFilters(self): + """ Saves the current filter list to backup storage.""" + self._filters_backup = self.filters[:] + + def RestoreFilters(self): + """ Restores filters previously backed up.""" + self.filters = self._filters_backup[:] + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + +def _AddFilters(filters): + """Adds more filter overrides. + + Unlike _SetFilters, this function does not reset the current list of filters + available. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.AddFilters(filters) + +def _BackupFilters(): + """ Saves the current filter list to backup storage.""" + _cpplint_state.BackupFilters() + +def _RestoreFilters(): + """ Restores filters previously backed up.""" + _cpplint_state.RestoreFilters() + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo(object): + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg")) and + not os.path.exists(os.path.join(root_dir, ".svn"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Match a single C style comment on the same line. +_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' +# Matches multi-line C style comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + + _RE_PATTERN_C_COMMENTS + r'\s+|' + + r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + + _RE_PATTERN_C_COMMENTS + r')') + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def CleanseRawStrings(raw_lines): + """Removes C++11 raw strings from lines. + + Before: + static const char kData[] = R"( + multi-line string + )"; + + After: + static const char kData[] = "" + (replaced by blank line) + ""; + + Args: + raw_lines: list of raw lines. + + Returns: + list of lines with C++11 raw strings replaced by empty strings. + """ + + delimiter = None + lines_without_raw_strings = [] + for line in raw_lines: + if delimiter: + # Inside a raw string, look for the end + end = line.find(delimiter) + if end >= 0: + # Found the end of the string, match leading space for this + # line and resume copying the original lines, and also insert + # a "" on the last line. + leading_space = Match(r'^(\s*)\S', line) + line = leading_space.group(1) + '""' + line[end + len(delimiter):] + delimiter = None + else: + # Haven't found the end yet, append a blank line. + line = '""' + + # Look for beginning of a raw string, and replace them with + # empty strings. This is done in a loop to handle multiple raw + # strings on the same line. + while delimiter is None: + # Look for beginning of a raw string. + # See 2.14.15 [lex.string] for syntax. + matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) + if matched: + delimiter = ')' + matched.group(2) + '"' + + end = matched.group(3).find(delimiter) + if end >= 0: + # Raw string ended on same line + line = (matched.group(1) + '""' + + matched.group(3)[end + len(delimiter):]) + delimiter = None + else: + # Start of a multi-line raw string + line = matched.group(1) + '""' + else: + break + + lines_without_raw_strings.append(line) + + # TODO(unknown): if delimiter is not None here, we might want to + # emit a warning for unterminated string. + return lines_without_raw_strings + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '/**/' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 4 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments. + 2) lines member contains lines without comments. + 3) raw_lines member contains all the lines without processing. + 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw + strings removed. + All these members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + self.lines_without_raw_strings = CleanseRawStrings(lines) + for linenum in range(len(self.lines_without_raw_strings)): + self.lines.append(CleanseComments( + self.lines_without_raw_strings[linenum])) + elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if _RE_PATTERN_INCLUDE.match(elided): + return elided + + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + + # Replace quoted strings and digit separators. Both single quotes + # and double quotes are processed in the same loop, otherwise + # nested quotes wouldn't work. + collapsed = '' + while True: + # Find the first quote character + match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) + if not match: + collapsed += elided + break + head, quote, tail = match.groups() + + if quote == '"': + # Collapse double quoted strings + second_quote = tail.find('"') + if second_quote >= 0: + collapsed += head + '""' + elided = tail[second_quote + 1:] + else: + # Unmatched double quote, don't bother processing the rest + # of the line since this is probably a multiline string. + collapsed += elided + break + else: + # Found single quote, check nearby text to eliminate digit separators. + # + # There is no special handling for floating point here, because + # the integer/fractional/exponent parts would all be parsed + # correctly as long as there are digits on both sides of the + # separator. So we are fine as long as we don't see something + # like "0.'3" (gcc 4.9.0 will not allow this literal). + if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): + match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) + collapsed += head + match_literal.group(1).replace("'", '') + elided = match_literal.group(2) + else: + second_quote = tail.find('\'') + if second_quote >= 0: + collapsed += head + "''" + elided = tail[second_quote + 1:] + else: + # Unmatched single quote + collapsed += elided + break + + return collapsed + + +def FindEndOfExpressionInLine(line, startpos, stack): + """Find the position just after the end of current parenthesized expression. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + stack: nesting stack at startpos. + + Returns: + On finding matching end: (index just after matching end, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at end of this line) + """ + for i in xrange(startpos, len(line)): + char = line[i] + if char in '([{': + # Found start of parenthesized expression, push to expression stack + stack.append(char) + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + if stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + elif i > 0 and Search(r'\boperator\s*$', line[0:i]): + # operator<, don't add to stack + continue + else: + # Tentative start of template argument list + stack.append('<') + elif char in ')]}': + # Found end of parenthesized expression. + # + # If we are currently expecting a matching '>', the pending '<' + # must have been an operator. Remove them from expression stack. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + if ((stack[-1] == '(' and char == ')') or + (stack[-1] == '[' and char == ']') or + (stack[-1] == '{' and char == '}')): + stack.pop() + if not stack: + return (i + 1, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == '>': + # Found potential end of template argument list. + + # Ignore "->" and operator functions + if (i > 0 and + (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): + continue + + # Pop the stack if there is a matching '<'. Otherwise, ignore + # this '>' since it must be an operator. + if stack: + if stack[-1] == '<': + stack.pop() + if not stack: + return (i + 1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '>', the matching '<' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + + # Did not find end of expression or unbalanced parentheses on this line + return (-1, stack) + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [ or <, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the + linenum/pos that correspond to the closing of the expression. + + TODO(unknown): cpplint spends a fair bit of time matching parentheses. + Ideally we would want to index all opening and closing parentheses once + and have CloseExpression be just a simple lookup, but due to preprocessor + tricks, this is not so easy. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): + return (line, clean_lines.NumLines(), -1) + + # Check first line + (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + + +def FindStartOfExpressionInLine(line, endpos, stack): + """Find position at the matching start of current expression. + + This is almost the reverse of FindEndOfExpressionInLine, but note + that the input position and returned position differs by 1. + + Args: + line: a CleansedLines line. + endpos: start searching at this position. + stack: nesting stack at endpos. + + Returns: + On finding matching start: (index at matching start, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at beginning of this line) + """ + i = endpos + while i >= 0: + char = line[i] + if char in ')]}': + # Found end of expression, push to expression stack + stack.append(char) + elif char == '>': + # Found potential end of template argument list. + # + # Ignore it if it's a "->" or ">=" or "operator>" + if (i > 0 and + (line[i - 1] == '-' or + Match(r'\s>=\s', line[i - 1:]) or + Search(r'\boperator\s*$', line[0:i]))): + i -= 1 + else: + stack.append('>') + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + i -= 1 + else: + # If there is a matching '>', we can pop the expression stack. + # Otherwise, ignore this '<' since it must be an operator. + if stack and stack[-1] == '>': + stack.pop() + if not stack: + return (i, None) + elif char in '([{': + # Found start of expression. + # + # If there are any unmatched '>' on the stack, they must be + # operators. Remove those. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + if ((char == '(' and stack[-1] == ')') or + (char == '[' and stack[-1] == ']') or + (char == '{' and stack[-1] == '}')): + stack.pop() + if not stack: + return (i, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '<', the matching '>' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + + i -= 1 + + return (-1, stack) + + +def ReverseCloseExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the opening of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *at* the opening brace, or + (line, 0, -1) if we never find the matching opening brace. Note + we ignore strings and comments when matching; and the line we + return is the 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + if line[pos] not in ')}]>': + return (line, 0, -1) + + # Check last line + (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) + if start_pos > -1: + return (line, linenum, start_pos) + + # Continue scanning backward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if start_pos > -1: + return (line, linenum, start_pos) + + # Did not find start of expression before beginning of file, give up + return (line, 0, -1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] "') + + +def GetIndentLevel(line): + """Return the number of leading spaces in line. + + Args: + line: A string to check. + + Returns: + An integer count of leading spaces, possibly zero. + """ + indent = Match(r'^( *)\S', line) + if indent: + return len(indent.group(1)) + else: + return 0 + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + # Replace 'c++' with 'cpp'. + filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RepositoryName() + if _root: + file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) + return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' + + +def CheckForHeaderGuard(filename, clean_lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + clean_lines: A CleansedLines instance containing the file. + error: The function to call with any errors found. + """ + + # Don't check for header guards if there are error suppression + # comments somewhere in this file. + # + # Because this is silencing a warning for a nonexistent line, we + # only support the very specific NOLINT(build/header_guard) syntax, + # and not the general NOLINT or NOLINT(*) syntax. + raw_lines = clean_lines.lines_without_raw_strings + for i in raw_lines: + if Search(r'//\s*NOLINT\(build/header_guard\)', i): + return + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = '' + ifndef_linenum = 0 + define = '' + endif = '' + endif_linenum = 0 + for linenum, line in enumerate(raw_lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef or not define or ifndef != define: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + # Check for "//" comments on endif line. + ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, + error) + match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) + if match: + if match.group(1) == '_': + # Issue low severity warning for deprecated double trailing underscore + error(filename, endif_linenum, 'build/header_guard', 0, + '#endif line should be "#endif // %s"' % cppvar) + return + + # Didn't find the corresponding "//" comment. If this file does not + # contain any "//" comments at all, it could be that the compiler + # only wants "/**/" comments, look for those instead. + no_single_line_comments = True + for i in xrange(1, len(raw_lines) - 1): + line = raw_lines[i] + if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): + no_single_line_comments = False + break + + if no_single_line_comments: + match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) + if match: + if match.group(1) == '_': + # Low severity warning for double trailing underscore + error(filename, endif_linenum, 'build/header_guard', 0, + '#endif line should be "#endif /* %s */"' % cppvar) + return + + # Didn't find anything + error(filename, endif_linenum, 'build/header_guard', 5, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckHeaderFileIncluded(filename, include_state, error): + """Logs an error if a .cc file does not include its header.""" + + # Do not check test files + if filename.endswith('_test.cc') or filename.endswith('_unittest.cc'): + return + + fileinfo = FileInfo(filename) + headerfile = filename[0:len(filename) - 2] + 'h' + if not os.path.exists(headerfile): + return + headername = FileInfo(headerfile).RepositoryName() + first_include = 0 + for section_list in include_state.include_list: + for f in section_list: + if headername in f[0] or f[0] in headername: + return + if not first_include: + first_include = f[1] + + error(filename, first_include, 'build/include', 5, + '%s should include its header file %s' % (fileinfo.RepositoryName(), + headername)) + + +def CheckForBadCharacters(filename, lines, error): + """Logs an error for each line containing bad characters. + + Two kinds of bad characters: + + 1. Unicode replacement characters: These indicate that either the file + contained invalid UTF-8 (likely) or Unicode replacement characters (which + it shouldn't). Note that it's possible for this to throw off line + numbering if the invalid UTF-8 occurred adjacent to a newline. + + 2. NUL bytes. These are problematic for some tools. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + if '\0' in line: + error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. ' + 'Use C++11 raw strings or concatenation instead.') + + +# (non-threadsafe name, thread-safe alternative, validation pattern) +# +# The validation pattern is used to eliminate false positives such as: +# _rand(); // false positive due to substring match. +# ->rand(); // some member function rand(). +# ACMRandom rand(seed); // some variable named rand. +# ISAACRandom rand(); // another variable named rand. +# +# Basically we require the return value of these functions to be used +# in some expression context on the same line by matching on some +# operator before the function name. This eliminates constructors and +# member function calls. +_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' +_THREADING_LIST = ( + ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), + ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), + ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), + ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), + ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), + ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), + ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), + ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), + ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), + ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), + ('strtok(', 'strtok_r(', + _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), + ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: + # Additional pattern matching check to confirm that this is the + # function we are looking for + if Search(pattern, line): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_func + + '...) instead of ' + single_thread_func + + '...) for improved thread safety.') + + +def CheckVlogArguments(filename, clean_lines, linenum, error): + """Checks that VLOG() is only used for defining a logging level. + + For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and + VLOG(FATAL) are not. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): + error(filename, linenum, 'runtime/vlog', 5, + 'VLOG() should be used with numeric verbosity level. ' + 'Use LOG() if you want symbolic severity levels.') + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +def IsMacroDefinition(clean_lines, linenum): + if Search(r'^#define', clean_lines[linenum]): + return True + + if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): + return True + + return False + + +def IsForwardClassDeclaration(clean_lines, linenum): + return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) + + +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, seen_open_brace): + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + self.check_namespace_indentation = False + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. + + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def IsBlockInfo(self): + """Returns true if this block is a _BlockInfo. + + This is convenient for verifying that an object is an instance of + a _BlockInfo, but not an instance of any of the derived classes. + + Returns: + True for this class, False for derived classes. + """ + return self.__class__ == _BlockInfo + + +class _ExternCInfo(_BlockInfo): + """Stores information about an 'extern "C"' block.""" + + def __init__(self): + _BlockInfo.__init__(self, True) + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, False) + self.name = name + self.starting_linenum = linenum + self.is_derived = False + self.check_namespace_indentation = True + if class_or_struct == 'struct': + self.access = 'public' + self.is_struct = True + else: + self.access = 'private' + self.is_struct = False + + # Remember initial indentation level for this class. Using raw_lines here + # instead of elided to account for leading comments. + self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + # If there is a DISALLOW macro, it should appear near the end of + # the class. + seen_last_thing_in_class = False + for i in xrange(linenum - 1, self.starting_linenum, -1): + match = Search( + r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + + self.name + r'\)', + clean_lines.elided[i]) + if match: + if seen_last_thing_in_class: + error(filename, i, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + break + + if not Match(r'^\s*$', clean_lines.elided[i]): + seen_last_thing_in_class = True + + # Check that closing brace is aligned with beginning of the class. + # Only do this if the closing brace is indented by only whitespaces. + # This means we will not check single-line class definitions. + indent = Match(r'^( *)\}', clean_lines.elided[linenum]) + if indent and len(indent.group(1)) != self.class_indent: + if self.is_struct: + parent = 'struct ' + self.name + else: + parent = 'class ' + self.name + error(filename, linenum, 'whitespace/indent', 3, + 'Closing brace should be aligned with beginning of %s' % parent) + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, False) + self.name = name or '' + self.starting_linenum = linenum + self.check_namespace_indentation = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. + # + # We also accept stuff like "// end of namespace ." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. + if self.name: + # Named namespace + if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + + r'[\*/\.\\\s]*$'), + line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace %s"' % + self.name) + else: + # Anonymous namespace + if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): + # If "// namespace anonymous" or "// anonymous namespace (more text)", + # mention "// anonymous namespace" as an acceptable form + if Match(r'}.*\b(namespace anonymous|anonymous namespace)\b', line): + error(filename, linenum, 'readability/namespace', 5, + 'Anonymous namespace should be terminated with "// namespace"' + ' or "// anonymous namespace"') + else: + error(filename, linenum, 'readability/namespace', 5, + 'Anonymous namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Top of the previous stack before each Update(). + # + # Because the nesting_stack is updated at the end of each line, we + # had to do some convoluted checks to find out what is the current + # scope at the beginning of the line. This check is simplified by + # saving the previous top of nesting stack. + # + # We could save the full stack, but we only need the top. Copying + # the full nesting stack would slow down cpplint by ~10%. + self.previous_stack_top = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def InExternC(self): + """Check if we are currently one level inside an 'extern "C"' block. + + Returns: + True if top of the stack is an extern block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ExternCInfo) + + def InClassDeclaration(self): + """Check if we are currently one level inside a class or struct declaration. + + Returns: + True if top of the stack is a class/struct, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ClassInfo) + + def InAsmBlock(self): + """Check if we are currently one level inside an inline ASM block. + + Returns: + True if the top of the stack is a block containing inline ASM. + """ + return self.stack and self.stack[-1].inline_asm != _NO_ASM + + def InTemplateArgumentList(self, clean_lines, linenum, pos): + """Check if current position is inside template argument list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: position just after the suspected template argument. + Returns: + True if (linenum, pos) is inside template arguments. + """ + while linenum < clean_lines.NumLines(): + # Find the earliest character that might indicate a template argument + line = clean_lines.elided[linenum] + match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) + if not match: + linenum += 1 + pos = 0 + continue + token = match.group(1) + pos += len(match.group(0)) + + # These things do not look like template argument list: + # class Suspect { + # class Suspect x; } + if token in ('{', '}', ';'): return False + + # These things look like template argument list: + # template + # template + # template + # template + if token in ('>', '=', '[', ']', '.'): return True + + # Check if token is an unmatched '<'. + # If not, move on to the next character. + if token != '<': + pos += 1 + if pos >= len(line): + linenum += 1 + pos = 0 + continue + + # We can't be sure if we just find a single '<', and need to + # find the matching '>'. + (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) + if end_pos < 0: + # Not sure if template argument list or syntax error in file + return False + linenum = end_line + pos = end_pos + return False + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + # TODO(unknown): Update() is too long, but we will refactor later. + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remember top of the previous nesting stack. + # + # The stack is always pushed/popped and not modified in place, so + # we can just do a shallow copy instead of copy.deepcopy. Using + # deepcopy would slow down cpplint by ~28%. + if self.stack: + self.previous_stack_top = self.stack[-1] + else: + self.previous_stack_top = None + + # Update pp_stack + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + class_decl_match = Match( + r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' + r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' + r'(.*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + # We do not want to accept classes that are actually template arguments: + # template , + # template class Ignore3> + # void Function() {}; + # + # To avoid template argument cases, we scan forward and look for + # an unmatched '>'. If we see one, assume we are inside a + # template argument list. + end_declaration = len(class_decl_match.group(1)) + if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): + self.stack.append(_ClassInfo( + class_decl_match.group(3), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(4) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + classinfo = self.stack[-1] + access_match = Match( + r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' + r':(?:[^:]|$)', + line) + if access_match: + classinfo.access = access_match.group(2) + + # Check that access keywords are indented +1 space. Skip this + # check if the keywords are not preceded by whitespaces. + indent = access_match.group(1) + if (len(indent) != classinfo.class_indent + 1 and + Match(r'^\s*$', indent)): + if classinfo.is_struct: + parent = 'struct ' + classinfo.name + else: + parent = 'class ' + classinfo.name + slots = '' + if access_match.group(3): + slots = access_match.group(3) + error(filename, linenum, 'whitespace/indent', 3, + '%s%s: should be indented +1 space inside %s' % ( + access_match.group(2), slots, parent)) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + elif Match(r'^extern\s*"[^"]*"\s*\{', line): + self.stack.append(_ExternCInfo()) + else: + self.stack.append(_BlockInfo(True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckCompletedBlocks(self, filename, error): + """Checks that all classes and namespaces have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + elif isinstance(obj, _NamespaceInfo): + error(filename, obj.starting_linenum, 'build/namespaces', 5, + 'Failed to find complete declaration of namespace %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: + return + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. Also look for + # non-single-argument constructors which are also technically valid, but + # strongly suggest something is wrong. + explicit_constructor_match = Match( + r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' + r'\(((?:[^()]|\([^()]*\))*)\)' + % re.escape(base_classname), + line) + + if explicit_constructor_match: + is_marked_explicit = explicit_constructor_match.group(1) + + if not explicit_constructor_match.group(2): + constructor_args = [] + else: + constructor_args = explicit_constructor_match.group(2).split(',') + + # collapse arguments so that commas in template parameter lists and function + # argument parameter lists don't split arguments in two + i = 0 + while i < len(constructor_args): + constructor_arg = constructor_args[i] + while (constructor_arg.count('<') > constructor_arg.count('>') or + constructor_arg.count('(') > constructor_arg.count(')')): + constructor_arg += ',' + constructor_args[i + 1] + del constructor_args[i + 1] + constructor_args[i] = constructor_arg + i += 1 + + defaulted_args = [arg for arg in constructor_args if '=' in arg] + noarg_constructor = (not constructor_args or # empty arg list + # 'void' arg specifier + (len(constructor_args) == 1 and + constructor_args[0].strip() == 'void')) + onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg + not noarg_constructor) or + # all but at most one arg defaulted + (len(constructor_args) >= 1 and + not noarg_constructor and + len(defaulted_args) >= len(constructor_args) - 1)) + initializer_list_constructor = bool( + onearg_constructor and + Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) + copy_constructor = bool( + onearg_constructor and + Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' + % re.escape(base_classname), constructor_args[0].strip())) + + if (not is_marked_explicit and + onearg_constructor and + not initializer_list_constructor and + not copy_constructor): + if defaulted_args: + error(filename, linenum, 'runtime/explicit', 5, + 'Constructors callable with one argument ' + 'should be marked explicit.') + else: + error(filename, linenum, 'runtime/explicit', 5, + 'Single-parameter constructors should be marked explicit.') + elif is_marked_explicit and not onearg_constructor: + if noarg_constructor: + error(filename, linenum, 'runtime/explicit', 5, + 'Zero-parameter constructors should not be marked explicit.') + else: + error(filename, linenum, 'runtime/explicit', 0, + 'Constructors that require multiple arguments ' + 'should not be marked explicit.') + + +def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', + fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and + not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and + not Search(r'\bcase\s+\(', fncall)): + # TODO(unknown): Space after an operator function seem to be a common + # error, silence those for now by restricting them to highest verbosity. + if Search(r'\boperator_*\b', line): + error(filename, linenum, 'whitespace/parens', 0, + 'Extra space before ( in function call') + else: + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error): + is_namespace_indent_item = ( + len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and + nesting_state.previous_stack_top == nesting_state.stack[-2]) + + if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + clean_lines.elided, line): + CheckItemIndentationInNamespace(filename, clean_lines.elided, + line, error) + + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(line, filename, linenum, next_line_start, error): + """Checks for common mistakes in comments. + + Args: + line: The line in question. + filename: The name of the current file. + linenum: The number of the line to check. + next_line_start: The first non-whitespace column of the next line. + error: The function to call with any errors found. + """ + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + + # Checks for common mistakes in TODO comments. + comment = line[commentpos:] + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + # If the comment contains an alphanumeric character, there + # should be a space somewhere between it and the // unless + # it's a /// or //! Doxygen comment. + if (Match(r'//[^ ]*\w', comment) and + not Match(r'(///|//\!)(\s+|$)', comment)): + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw = clean_lines.lines_without_raw_strings + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + # + # Also skip blank line checks for 'extern "C"' blocks, which are formatted + # like namespaces. + if (IsBlankLine(line) and + not nesting_state.InNamespaceBody() and + not nesting_state.InExternC()): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Redundant blank line at the start of a code block ' + 'should be deleted.') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Redundant blank line at the end of a code block ' + 'should be deleted.') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, check comments + next_line_start = 0 + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + next_line_start = len(next_line) - len(next_line.lstrip()) + CheckComment(line, filename, linenum, next_line_start, error) + + # get rid of comments and strings + line = clean_lines.elided[linenum] + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'return []() {};' + if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search(r'for *\(.*[^:]:[^: ]', line) or + Search(r'for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckOperatorSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around operators. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Don't try to do spacing checks for operator methods. Do this by + # replacing the troublesome characters with something else, + # preserving column position for all other characters. + # + # The replacement is done repeatedly to avoid false positives from + # operators that call operators. + while True: + match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) + if match: + line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) + else: + break + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if ((Search(r'[\w.]=', line) or + Search(r'=[\w.]', line)) + and not Search(r'\b(if|while|for) ', line) + # Operators taken from [lex.operators] in C++11 standard. + and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) + and not Search(r'operator=', line)): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + # + # If the operator is followed by a comma, assume it's be used in a + # macro context and don't do any checks. This avoids false + # positives. + # + # Note that && is not included here. Those are checked separately + # in CheckRValueReference + match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + elif not Match(r'#.*include', line): + # Look for < that is not surrounded by spaces. This is only + # triggered if both sides are missing spaces, even though + # technically should should flag if at least one side is missing a + # space. This is done to avoid some false positives with shifts. + match = Match(r'^(.*[^\s<])<[^\s=<,]', line) + if match: + (_, _, end_pos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if end_pos <= -1: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <') + + # Look for > that is not surrounded by spaces. Similar to the + # above, we only trigger if both sides are missing spaces to avoid + # false positives with shifts. + match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) + if match: + (_, _, start_pos) = ReverseCloseExpression( + clean_lines, linenum, len(match.group(1))) + if start_pos <= -1: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >') + + # We allow no-spaces around << when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + # + # We also allow operators following an opening parenthesis, since + # those tend to be macros that deal with operators. + match = Search(r'(operator|[^\s(<])(?:L|UL|ULL|l|ul|ull)?<<([^\s,=<])', line) + if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and + not (match.group(1) == 'operator' and match.group(2) == ';')): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <<') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + +def CheckParenthesisSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around parentheses. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # No spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if len(match.group(2)) not in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + +def CheckCommaSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing near commas and semicolons. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.lines_without_raw_strings + line = clean_lines.elided[linenum] + + # You should always have a space after a comma (either as fn arg or operator) + # + # This does not apply when the non-space character following the + # comma is another comma, since the only time when that happens is + # for empty macro arguments. + # + # We run this check in two passes: first pass on elided lines to + # verify that lines contain missing whitespaces, second pass on raw + # lines to confirm that those missing whitespaces are not due to + # elided comments. + if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and + Search(r',[^,\s]', raw[linenum])): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + +def CheckBracesSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing near commas. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces. And since you should never have braces at the beginning of a line, + # this is an easy test. + match = Match(r'^(.*[^ ({>]){', line) + if match: + # Try a bit harder to check for brace initialization. This + # happens in one of the following forms: + # Constructor() : initializer_list_{} { ... } + # Constructor{}.MemberFunction() + # Type variable{}; + # FunctionCall(type{}, ...); + # LastArgument(..., type{}); + # LOG(INFO) << type{} << " ..."; + # map_of_type[{...}] = ...; + # ternary = expr ? new type{} : nullptr; + # OuterTemplate{}> + # + # We check for the character following the closing brace, and + # silence the warning if it's one of those listed above, i.e. + # "{.;,)<>]:". + # + # To account for nested initializer list, we allow any number of + # closing braces up to "{;,)<". We can't simply silence the + # warning on first sight of closing brace, because that would + # cause false negatives for things that are not initializer lists. + # Silence this: But not this: + # Outer{ if (...) { + # Inner{...} if (...){ // Missing space before { + # }; } + # + # There is a false negative with this approach if people inserted + # spurious semicolons, e.g. "if (cond){};", but we will catch the + # spurious semicolon with a separate check. + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + trailing_text = '' + if endpos > -1: + trailing_text = endline[endpos:] + for offset in xrange(endlinenum + 1, + min(endlinenum + 3, clean_lines.NumLines() - 1)): + trailing_text += clean_lines.elided[offset] + if not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use {} instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + +def IsDecltype(clean_lines, linenum, column): + """Check if the token ending on (linenum, column) is decltype(). + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is decltype() expression, False otherwise. + """ + (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) + if start_col < 0: + return False + if Search(r'\bdecltype\s*$', text[0:start_col]): + return True + return False + + +def IsTemplateParameterList(clean_lines, linenum, column): + """Check if the token ending on (linenum, column) is the end of template<>. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is end of a template parameter list, False otherwise. + """ + (_, startline, startpos) = ReverseCloseExpression( + clean_lines, linenum, column) + if (startpos > -1 and + Search(r'\btemplate\s*$', clean_lines.elided[startline][0:startpos])): + return True + return False + + +def IsRValueType(typenames, clean_lines, nesting_state, linenum, column): + """Check if the token ending on (linenum, column) is a type. + + Assumes that text to the right of the column is "&&" or a function + name. + + Args: + typenames: set of type names from template-argument-list. + clean_lines: A CleansedLines instance containing the file. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is a type, False if we are not sure. + """ + prefix = clean_lines.elided[linenum][0:column] + + # Get one word to the left. If we failed to do so, this is most + # likely not a type, since it's unlikely that the type name and "&&" + # would be split across multiple lines. + match = Match(r'^(.*)(\b\w+|[>*)&])\s*$', prefix) + if not match: + return False + + # Check text following the token. If it's "&&>" or "&&," or "&&...", it's + # most likely a rvalue reference used inside a template. + suffix = clean_lines.elided[linenum][column:] + if Match(r'&&\s*(?:[>,]|\.\.\.)', suffix): + return True + + # Check for known types and end of templates: + # int&& variable + # vector&& variable + # + # Because this function is called recursively, we also need to + # recognize pointer and reference types: + # int* Function() + # int& Function() + if (match.group(2) in typenames or + match.group(2) in ['char', 'char16_t', 'char32_t', 'wchar_t', 'bool', + 'short', 'int', 'long', 'signed', 'unsigned', + 'float', 'double', 'void', 'auto', '>', '*', '&']): + return True + + # If we see a close parenthesis, look for decltype on the other side. + # decltype would unambiguously identify a type, anything else is + # probably a parenthesized expression and not a type. + if match.group(2) == ')': + return IsDecltype( + clean_lines, linenum, len(match.group(1)) + len(match.group(2)) - 1) + + # Check for casts and cv-qualifiers. + # match.group(1) remainder + # -------------- --------- + # const_cast< type&& + # const type&& + # type const&& + if Search(r'\b(?:const_cast\s*<|static_cast\s*<|dynamic_cast\s*<|' + r'reinterpret_cast\s*<|\w+\s)\s*$', + match.group(1)): + return True + + # Look for a preceding symbol that might help differentiate the context. + # These are the cases that would be ambiguous: + # match.group(1) remainder + # -------------- --------- + # Call ( expression && + # Declaration ( type&& + # sizeof ( type&& + # if ( expression && + # while ( expression && + # for ( type&& + # for( ; expression && + # statement ; type&& + # block { type&& + # constructor { expression && + start = linenum + line = match.group(1) + match_symbol = None + while start >= 0: + # We want to skip over identifiers and commas to get to a symbol. + # Commas are skipped so that we can find the opening parenthesis + # for function parameter lists. + match_symbol = Match(r'^(.*)([^\w\s,])[\w\s,]*$', line) + if match_symbol: + break + start -= 1 + line = clean_lines.elided[start] + + if not match_symbol: + # Probably the first statement in the file is an rvalue reference + return True + + if match_symbol.group(2) == '}': + # Found closing brace, probably an indicate of this: + # block{} type&& + return True + + if match_symbol.group(2) == ';': + # Found semicolon, probably one of these: + # for(; expression && + # statement; type&& + + # Look for the previous 'for(' in the previous lines. + before_text = match_symbol.group(1) + for i in xrange(start - 1, max(start - 6, 0), -1): + before_text = clean_lines.elided[i] + before_text + if Search(r'for\s*\([^{};]*$', before_text): + # This is the condition inside a for-loop + return False + + # Did not find a for-init-statement before this semicolon, so this + # is probably a new statement and not a condition. + return True + + if match_symbol.group(2) == '{': + # Found opening brace, probably one of these: + # block{ type&& = ... ; } + # constructor{ expression && expression } + + # Look for a closing brace or a semicolon. If we see a semicolon + # first, this is probably a rvalue reference. + line = clean_lines.elided[start][0:len(match_symbol.group(1)) + 1] + end = start + depth = 1 + while True: + for ch in line: + if ch == ';': + return True + elif ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + if depth == 0: + return False + end += 1 + if end >= clean_lines.NumLines(): + break + line = clean_lines.elided[end] + # Incomplete program? + return False + + if match_symbol.group(2) == '(': + # Opening parenthesis. Need to check what's to the left of the + # parenthesis. Look back one extra line for additional context. + before_text = match_symbol.group(1) + if linenum > 1: + before_text = clean_lines.elided[linenum - 1] + before_text + before_text = match_symbol.group(1) + + # Patterns that are likely to be types: + # [](type&& + # for (type&& + # sizeof(type&& + # operator=(type&& + # + if Search(r'(?:\]|\bfor|\bsizeof|\boperator\s*\S+\s*)\s*$', before_text): + return True + + # Patterns that are likely to be expressions: + # if (expression && + # while (expression && + # : initializer(expression && + # , initializer(expression && + # ( FunctionCall(expression && + # + FunctionCall(expression && + # + (expression && + # + # The last '+' represents operators such as '+' and '-'. + if Search(r'(?:\bif|\bwhile|[-+=%^(]*>)?\s*$', + match_symbol.group(1)) + if match_func: + # Check for constructors, which don't have return types. + if Search(r'\b(?:explicit|inline)$', match_func.group(1)): + return True + implicit_constructor = Match(r'\s*(\w+)\((?:const\s+)?(\w+)', prefix) + if (implicit_constructor and + implicit_constructor.group(1) == implicit_constructor.group(2)): + return True + return IsRValueType(typenames, clean_lines, nesting_state, linenum, + len(match_func.group(1))) + + # Nothing before the function name. If this is inside a block scope, + # this is probably a function call. + return not (nesting_state.previous_stack_top and + nesting_state.previous_stack_top.IsBlockInfo()) + + if match_symbol.group(2) == '>': + # Possibly a closing bracket, check that what's on the other side + # looks like the start of a template. + return IsTemplateParameterList( + clean_lines, start, len(match_symbol.group(1))) + + # Some other symbol, usually something like "a=b&&c". This is most + # likely not a type. + return False + + +def IsDeletedOrDefault(clean_lines, linenum): + """Check if current constructor or operator is deleted or default. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if this is a deleted or default constructor. + """ + open_paren = clean_lines.elided[linenum].find('(') + if open_paren < 0: + return False + (close_line, _, close_paren) = CloseExpression( + clean_lines, linenum, open_paren) + if close_paren < 0: + return False + return Match(r'\s*=\s*(?:delete|default)\b', close_line[close_paren:]) + + +def IsRValueAllowed(clean_lines, linenum, typenames): + """Check if RValue reference is allowed on a particular line. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + typenames: set of type names from template-argument-list. + Returns: + True if line is within the region where RValue references are allowed. + """ + # Allow region marked by PUSH/POP macros + for i in xrange(linenum, 0, -1): + line = clean_lines.elided[i] + if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): + if not line.endswith('PUSH'): + return False + for j in xrange(linenum, clean_lines.NumLines(), 1): + line = clean_lines.elided[j] + if Match(r'GOOGLE_ALLOW_RVALUE_REFERENCES_(?:PUSH|POP)', line): + return line.endswith('POP') + + # Allow operator= + line = clean_lines.elided[linenum] + if Search(r'\boperator\s*=\s*\(', line): + return IsDeletedOrDefault(clean_lines, linenum) + + # Allow constructors + match = Match(r'\s*(?:[\w<>]+::)*([\w<>]+)\s*::\s*([\w<>]+)\s*\(', line) + if match and match.group(1) == match.group(2): + return IsDeletedOrDefault(clean_lines, linenum) + if Search(r'\b(?:explicit|inline)\s+[\w<>]+\s*\(', line): + return IsDeletedOrDefault(clean_lines, linenum) + + if Match(r'\s*[\w<>]+\s*\(', line): + previous_line = 'ReturnType' + if linenum > 0: + previous_line = clean_lines.elided[linenum - 1] + if Match(r'^\s*$', previous_line) or Search(r'[{}:;]\s*$', previous_line): + return IsDeletedOrDefault(clean_lines, linenum) + + # Reject types not mentioned in template-argument-list + while line: + match = Match(r'^.*?(\w+)\s*&&(.*)$', line) + if not match: + break + if match.group(1) not in typenames: + return False + line = match.group(2) + + # All RValue types that were in template-argument-list should have + # been removed by now. Those were allowed, assuming that they will + # be forwarded. + # + # If there are no remaining RValue types left (i.e. types that were + # not found in template-argument-list), flag those as not allowed. + return line.find('&&') < 0 + + +def GetTemplateArgs(clean_lines, linenum): + """Find list of template arguments associated with this function declaration. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Line number containing the start of the function declaration, + usually one line after the end of the template-argument-list. + Returns: + Set of type names, or empty set if this does not appear to have + any template parameters. + """ + # Find start of function + func_line = linenum + while func_line > 0: + line = clean_lines.elided[func_line] + if Match(r'^\s*$', line): + return set() + if line.find('(') >= 0: + break + func_line -= 1 + if func_line == 0: + return set() + + # Collapse template-argument-list into a single string + argument_list = '' + match = Match(r'^(\s*template\s*)<', clean_lines.elided[func_line]) + if match: + # template-argument-list on the same line as function name + start_col = len(match.group(1)) + _, end_line, end_col = CloseExpression(clean_lines, func_line, start_col) + if end_col > -1 and end_line == func_line: + start_col += 1 # Skip the opening bracket + argument_list = clean_lines.elided[func_line][start_col:end_col] + + elif func_line > 1: + # template-argument-list one line before function name + match = Match(r'^(.*)>\s*$', clean_lines.elided[func_line - 1]) + if match: + end_col = len(match.group(1)) + _, start_line, start_col = ReverseCloseExpression( + clean_lines, func_line - 1, end_col) + if start_col > -1: + start_col += 1 # Skip the opening bracket + while start_line < func_line - 1: + argument_list += clean_lines.elided[start_line][start_col:] + start_col = 0 + start_line += 1 + argument_list += clean_lines.elided[func_line - 1][start_col:end_col] + + if not argument_list: + return set() + + # Extract type names + typenames = set() + while True: + match = Match(r'^[,\s]*(?:typename|class)(?:\.\.\.)?\s+(\w+)(.*)$', + argument_list) + if not match: + break + typenames.add(match.group(1)) + argument_list = match.group(2) + return typenames + + +def CheckRValueReference(filename, clean_lines, linenum, nesting_state, error): + """Check for rvalue references. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # Find lines missing spaces around &&. + # TODO(unknown): currently we don't check for rvalue references + # with spaces surrounding the && to avoid false positives with + # boolean expressions. + line = clean_lines.elided[linenum] + match = Match(r'^(.*\S)&&', line) + if not match: + match = Match(r'(.*)&&\S', line) + if (not match) or '(&&)' in line or Search(r'\boperator\s*$', match.group(1)): + return + + # Either poorly formed && or an rvalue reference, check the context + # to get a more accurate error message. Mostly we want to determine + # if what's to the left of "&&" is a type or not. + typenames = GetTemplateArgs(clean_lines, linenum) + and_pos = len(match.group(1)) + if IsRValueType(typenames, clean_lines, nesting_state, linenum, and_pos): + if not IsRValueAllowed(clean_lines, linenum, typenames): + error(filename, linenum, 'build/c++11', 3, + 'RValue references are an unapproved C++ feature.') + else: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around &&') + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone is using + # braces in a block to explicitly create a new scope, which is commonly used + # to control the lifetime of stack-allocated variables. Braces are also + # used for brace initializers inside function calls. We don't detect this + # perfectly: we just don't complain if the last non-whitespace character on + # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[,;:}{(]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'else if\s*\(', line): # could be multi-line if + brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + brace_on_right = endline[endpos:].find('{') != -1 + if brace_on_left != brace_on_right: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Check single-line if/else bodies. The style guide says 'curly braces are not + # required for single-line statements'. We additionally allow multi-line, + # single statements, but we reject anything with more than one semicolon in + # it. This means that the first semicolon after the if should be at the end of + # its line, and the line after that should have an indent level equal to or + # lower than the if. We also check for ambiguous if/else nesting without + # braces. + if_else_match = Search(r'\b(if\s*\(|else\b)', line) + if if_else_match and not Match(r'\s*#', line): + if_indent = GetIndentLevel(line) + endline, endlinenum, endpos = line, linenum, if_else_match.end() + if_match = Search(r'\bif\s*\(', line) + if if_match: + # This could be a multiline if condition, so find the end first. + pos = if_match.end() - 1 + (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) + # Check for an opening brace, either directly after the if or on the next + # line. If found, this isn't a single-statement conditional. + if (not Match(r'\s*{', endline[endpos:]) + and not (Match(r'\s*$', endline[endpos:]) + and endlinenum < (len(clean_lines.elided) - 1) + and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): + while (endlinenum < len(clean_lines.elided) + and ';' not in clean_lines.elided[endlinenum][endpos:]): + endlinenum += 1 + endpos = 0 + if endlinenum < len(clean_lines.elided): + endline = clean_lines.elided[endlinenum] + # We allow a mix of whitespace and closing braces (e.g. for one-liner + # methods) and a single \ after the semicolon (for macros) + endpos = endline.find(';') + if not Match(r';[\s}]*(\\?)$', endline[endpos:]): + # Semicolon isn't the last character, there's something trailing. + # Output a warning if the semicolon is not contained inside + # a lambda expression. + if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', + endline): + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + elif endlinenum < len(clean_lines.elided) - 1: + # Make sure the next line is dedented + next_line = clean_lines.elided[endlinenum + 1] + next_indent = GetIndentLevel(next_line) + # With ambiguous nested if statements, this will error out on the + # if that *doesn't* match the else, regardless of whether it's the + # inner one or outer one. + if (if_match and Match(r'\s*else\b', next_line) + and next_indent != if_indent): + error(filename, linenum, 'readability/braces', 4, + 'Else clause should be indented at the same level as if. ' + 'Ambiguous nested if/else chains require braces.') + elif next_indent > if_indent: + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + + +def CheckTrailingSemicolon(filename, clean_lines, linenum, error): + """Looks for redundant trailing semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] + + # Block bodies should not be followed by a semicolon. Due to C++11 + # brace initialization, there are more places where semicolons are + # required than not, so we use a whitelist approach to check these + # rather than a blacklist. These are the places where "};" should + # be replaced by just "}": + # 1. Some flavor of block following closing parenthesis: + # for (;;) {}; + # while (...) {}; + # switch (...) {}; + # Function(...) {}; + # if (...) {}; + # if (...) else if (...) {}; + # + # 2. else block: + # if (...) else {}; + # + # 3. const member function: + # Function(...) const {}; + # + # 4. Block following some statement: + # x = 42; + # {}; + # + # 5. Block at the beginning of a function: + # Function(...) { + # {}; + # } + # + # Note that naively checking for the preceding "{" will also match + # braces inside multi-dimensional arrays, but this is fine since + # that expression will not contain semicolons. + # + # 6. Block following another block: + # while (true) {} + # {}; + # + # 7. End of namespaces: + # namespace {}; + # + # These semicolons seems far more common than other kinds of + # redundant semicolons, possibly due to people converting classes + # to namespaces. For now we do not warn for this case. + # + # Try matching case 1 first. + match = Match(r'^(.*\)\s*)\{', line) + if match: + # Matched closing parenthesis (case 1). Check the token before the + # matching opening parenthesis, and don't warn if it looks like a + # macro. This avoids these false positives: + # - macro that defines a base class + # - multi-line macro that defines a base class + # - macro that defines the whole class-head + # + # But we still issue warnings for macros that we know are safe to + # warn, specifically: + # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P + # - TYPED_TEST + # - INTERFACE_DEF + # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: + # + # We implement a whitelist of safe macros instead of a blacklist of + # unsafe macros, even though the latter appears less frequently in + # google code and would have been easier to implement. This is because + # the downside for getting the whitelist wrong means some extra + # semicolons, while the downside for getting the blacklist wrong + # would result in compile errors. + # + # In addition to macros, we also don't want to warn on + # - Compound literals + # - Lambdas + # - alignas specifier with anonymous structs: + closing_brace_pos = match.group(1).rfind(')') + opening_parenthesis = ReverseCloseExpression( + clean_lines, linenum, closing_brace_pos) + if opening_parenthesis[2] > -1: + line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] + macro = Search(r'\b([A-Z_]+)\s*$', line_prefix) + func = Match(r'^(.*\])\s*$', line_prefix) + if ((macro and + macro.group(1) not in ( + 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', + 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', + 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or + (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or + Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or + Search(r'\s+=\s*$', line_prefix)): + match = None + if (match and + opening_parenthesis[1] > 1 and + Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): + # Multi-line lambda-expression + match = None + + else: + # Try matching cases 2-3. + match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + if not match: + # Try matching cases 4-6. These are always matched on separate lines. + # + # Note that we can't simply concatenate the previous line to the + # current line and do a single match, otherwise we may output + # duplicate warnings for the blank line case: + # if (cond) { + # // blank line + # } + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if prevline and Search(r'[;{}]\s*$', prevline): + match = Match(r'^(\s*)\{', line) + + # Check matching closing brace + if match: + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + # Current {} pair is eligible for semicolon check, and we have found + # the redundant semicolon, output warning here. + # + # Note: because we are scanning forward for opening braces, and + # outputting warnings for the matching closing brace, if there are + # nested blocks with trailing semicolons, we will get the error + # messages in reversed order. + error(filename, endlinenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyBlockBody(filename, clean_lines, linenum, error): + """Look for empty loop/conditional body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + # + # We also check "if" blocks here, since an empty conditional block + # is likely an error. + line = clean_lines.elided[linenum] + matched = Match(r'\s*(for|while|if)\s*\(', line) + if matched: + # Find the end of the conditional expression + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if matched.group(1) == 'if': + error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, + 'Empty conditional bodies should use {}') + else: + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + +def FindCheckMacro(line): + """Find a replaceable CHECK-like macro. + + Args: + line: line to search on. + Returns: + (macro name, start position), or (None, -1) if no replaceable + macro is found. + """ + for macro in _CHECK_MACROS: + i = line.find(macro) + if i >= 0: + # Find opening parenthesis. Do a regular expression match here + # to make sure that we are matching the expected CHECK macro, as + # opposed to some other macro that happens to contain the CHECK + # substring. + matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) + if not matched: + continue + return (macro, len(matched.group(1))) + return (None, -1) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + lines = clean_lines.elided + (check_macro, start_pos) = FindCheckMacro(lines[linenum]) + if not check_macro: + return + + # Find end of the boolean expression by matching parentheses + (last_line, end_line, end_pos) = CloseExpression( + clean_lines, linenum, start_pos) + if end_pos < 0: + return + + # If the check macro is followed by something other than a + # semicolon, assume users will log their own custom error messages + # and don't suggest any replacements. + if not Match(r'\s*;', last_line[end_pos:]): + return + + if linenum == end_line: + expression = lines[linenum][start_pos + 1:end_pos - 1] + else: + expression = lines[linenum][start_pos + 1:] + for i in xrange(linenum + 1, end_line): + expression += lines[i] + expression += last_line[0:end_pos - 1] + + # Parse expression so that we can take parentheses into account. + # This avoids false positives for inputs like "CHECK((a < 4) == b)", + # which is not replaceable by CHECK_LE. + lhs = '' + rhs = '' + operator = None + while expression: + matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' + r'==|!=|>=|>|<=|<|\()(.*)$', expression) + if matched: + token = matched.group(1) + if token == '(': + # Parenthesized operand + expression = matched.group(2) + (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) + if end < 0: + return # Unmatched parenthesis + lhs += '(' + expression[0:end] + expression = expression[end:] + elif token in ('&&', '||'): + # Logical and/or operators. This means the expression + # contains more than one term, for example: + # CHECK(42 < a && a < b); + # + # These are not replaceable with CHECK_LE, so bail out early. + return + elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): + # Non-relational operator + lhs += token + expression = matched.group(2) + else: + # Relational operator + operator = token + rhs = matched.group(2) + break + else: + # Unparenthesized operand. Instead of appending to lhs one character + # at a time, we do another regular expression match to consume several + # characters at once if possible. Trivial benchmark shows that this + # is more efficient when the operands are longer than a single + # character, which is generally the case. + matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) + if not matched: + matched = Match(r'^(\s*\S)(.*)$', expression) + if not matched: + break + lhs += matched.group(1) + expression = matched.group(2) + + # Only apply checks if we got all parts of the boolean expression + if not (lhs and operator and rhs): + return + + # Check that rhs do not contain logical operators. We already know + # that lhs is fine since the loop above parses out && and ||. + if rhs.find('&&') > -1 or rhs.find('||') > -1: + return + + # At least one of the operands must be a constant literal. This is + # to avoid suggesting replacements for unprintable things like + # CHECK(variable != iterator) + # + # The following pattern matches decimal, hex integers, strings, and + # characters (in that order). + lhs = lhs.strip() + rhs = rhs.strip() + match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' + if Match(match_constant, lhs) or Match(match_constant, rhs): + # Note: since we know both lhs and rhs, we can provide a more + # descriptive error message like: + # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) + # Instead of: + # Consider using CHECK_EQ instead of CHECK(a == b) + # + # We are still keeping the less descriptive message because if lhs + # or rhs gets long, the error message might become unreadable. + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[check_macro][operator], + check_macro, operator)) + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw_lines = clean_lines.lines_without_raw_strings + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' + classinfo = nesting_state.InnermostClass() + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for + # section labels, and also lines containing multi-line raw strings. + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(scope_or_label_pattern, cleansed_line) and + not (clean_lines.raw_lines[linenum] != line and + Match(r'^\s*""', line))): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + extended_length = int((_line_length * 1.25)) + if line_width > extended_length: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than %i characters' % + extended_length) + elif line_width > _line_length: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= %i characters long' % _line_length) + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckTrailingSemicolon(filename, clean_lines, linenum, error) + CheckEmptyBlockBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckOperatorSpacing(filename, clean_lines, linenum, error) + CheckParenthesisSpacing(filename, clean_lines, linenum, error) + CheckCommaSpacing(filename, clean_lines, linenum, error) + CheckBracesSpacing(filename, clean_lines, linenum, error) + CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) + CheckRValueReference(filename, clean_lines, linenum, nesting_state, error) + CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) + + +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input filename. + + Returns: + True if 'filename' looks like a test, False otherwise. + """ + if (filename.endswith('_test.cc') or + filename.endswith('_unittest.cc') or + filename.endswith('_regtest.cc')): + return True + else: + return False + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_cpp_h = include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + # Only do this check if the included header follows google naming + # conventions. If not, assume that it's a 3rd party API that + # requires special include conventions. + # + # We also make an exception for Lua headers, which follow google + # naming convention but not the include convention. + match = Match(r'#include\s*"([^/]+\.h)"', line) + if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + duplicate_line = include_state.FindHeader(include) + if duplicate_line >= 0: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, duplicate_line)) + elif (include.endswith('.cc') and + os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): + error(filename, linenum, 'build/include', 4, + 'Do not include .cc files from other packages') + elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): + include_state.include_list[-1].append((include, linenum)) + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) + if not include_state.IsInAlphabeticalOrder( + clean_lines, linenum, canonical_include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + include_state.SetLastHeader(canonical_include) + + + +def _GetTextInside(text, start_pattern): + r"""Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(unknown): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +# Patterns for matching call-by-reference parameters. +# +# Supports nested templates up to 2 levels deep using this messy pattern: +# < (?: < (?: < [^<>]* +# > +# | [^<>] )* +# > +# | [^<>] )* +# > +_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* +_RE_PATTERN_TYPE = ( + r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' + r'(?:\w|' + r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' + r'::)+') +# A call-by-reference parameter ends with '& identifier'. +_RE_PATTERN_REF_PARAM = re.compile( + r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' + r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') +# A call-by-const-reference parameter either ends with 'const& identifier' +# or looks like 'const type& identifier' when 'type' is atomic. +_RE_PATTERN_CONST_REF_PARAM = ( + r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + + r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, + include_state, nesting_state, error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Reset include state across preprocessor directives. This is meant + # to silence warnings for conditional includes. + match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) + if match: + include_state.ResetSection(match.group(1)) + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # Perform other checks now that we are sure that this is not an include line + CheckCasts(filename, clean_lines, linenum, error) + CheckGlobalStatic(filename, clean_lines, linenum, error) + CheckPrintf(filename, clean_lines, linenum, error) + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes declare or disable copy/assign + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(unknown): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckGlobalStatic(filename, clean_lines, linenum, error): + """Check for unsafe global or static objects. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Match two lines at a time to support multiline declarations + if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): + line += clean_lines.elided[linenum + 1].strip() + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + + # Remove false positives: + # - String pointers (as opposed to values). + # string *pointer + # const string *pointer + # string const *pointer + # string *const pointer + # + # - Functions and template specializations. + # string Function(... + # string Class::Method(... + # + # - Operators. These are matched separately because operator names + # cross non-word boundaries, and trying to match both operators + # and functions at the same time would decrease accuracy of + # matching identifiers. + # string Class::operator*() + if (match and + not Search(r'\bstring\b(\s+const)?\s*\*\s*(const\s+)?\w', line) and + not Search(r'\boperator\W', line) and + not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(3))): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + +def CheckPrintf(filename, clean_lines, linenum, error): + """Check for printf related issues. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\s*\(', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\s*\(', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + +def IsDerivedFunction(clean_lines, linenum): + """Check if current line contains an inherited function. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains a function with "override" + virt-specifier. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) + if match: + # Look for "override" after the matching closing parenthesis + line, _, closing_paren = CloseExpression( + clean_lines, i, len(match.group(1))) + return (closing_paren >= 0 and + Search(r'\boverride\b', line[closing_paren:])) + return False + + +def IsOutOfLineMethodDefinition(clean_lines, linenum): + """Check if current line contains an out-of-line method definition. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains an out-of-line method definition. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): + return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None + return False + + +def IsInitializerList(clean_lines, linenum): + """Check if current line is inside constructor initializer list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line appears to be inside constructor initializer + list, False otherwise. + """ + for i in xrange(linenum, 1, -1): + line = clean_lines.elided[i] + if i == linenum: + remove_function_body = Match(r'^(.*)\{\s*$', line) + if remove_function_body: + line = remove_function_body.group(1) + + if Search(r'\s:\s*\w+[({]', line): + # A lone colon tend to indicate the start of a constructor + # initializer list. It could also be a ternary operator, which + # also tend to appear in constructor initializer lists as + # opposed to parameter lists. + return True + if Search(r'\}\s*,\s*$', line): + # A closing brace followed by a comma is probably the end of a + # brace-initialized member in constructor initializer list. + return True + if Search(r'[{};]\s*$', line): + # Found one of the following: + # - A closing brace or semicolon, probably the end of the previous + # function. + # - An opening brace, probably the start of current class or namespace. + # + # Current line is probably not inside an initializer list since + # we saw one of those things without seeing the starting colon. + return False + + # Got to the beginning of the file without seeing the start of + # constructor initializer list. + return False + + +def CheckForNonConstReference(filename, clean_lines, linenum, + nesting_state, error): + """Check for non-const references. + + Separate from CheckLanguage since it scans backwards from current + line, instead of scanning forward. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # Do nothing if there is no '&' on current line. + line = clean_lines.elided[linenum] + if '&' not in line: + return + + # If a function is inherited, current function doesn't have much of + # a choice, so any non-const references should not be blamed on + # derived function. + if IsDerivedFunction(clean_lines, linenum): + return + + # Don't warn on out-of-line method definitions, as we would warn on the + # in-line declaration, if it isn't marked with 'override'. + if IsOutOfLineMethodDefinition(clean_lines, linenum): + return + + # Long type names may be broken across multiple lines, usually in one + # of these forms: + # LongType + # ::LongTypeContinued &identifier + # LongType:: + # LongTypeContinued &identifier + # LongType< + # ...>::LongTypeContinued &identifier + # + # If we detected a type split across two lines, join the previous + # line to current line so that we can match const references + # accordingly. + # + # Note that this only scans back one line, since scanning back + # arbitrary number of lines would be expensive. If you have a type + # that spans more than 2 lines, please use a typedef. + if linenum > 1: + previous = None + if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): + # previous_line\n + ::current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', + clean_lines.elided[linenum - 1]) + elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): + # previous_line::\n + current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', + clean_lines.elided[linenum - 1]) + if previous: + line = previous.group(1) + line.lstrip() + else: + # Check for templated parameter that is split across multiple lines + endpos = line.rfind('>') + if endpos > -1: + (_, startline, startpos) = ReverseCloseExpression( + clean_lines, linenum, endpos) + if startpos > -1 and startline < linenum: + # Found the matching < on an earlier line, collect all + # pieces up to current line. + line = '' + for i in xrange(startline, linenum + 1): + line += clean_lines.elided[i].strip() + + # Check for non-const references in function parameters. A single '&' may + # found in the following places: + # inside expression: binary & for bitwise AND + # inside expression: unary & for taking the address of something + # inside declarators: reference parameter + # We will exclude the first two cases by checking that we are not inside a + # function body, including one that was just introduced by a trailing '{'. + # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. + if (nesting_state.previous_stack_top and + not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or + isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): + # Not at toplevel, not within a class, and not within a namespace + return + + # Avoid initializer lists. We only need to scan back from the + # current line for something that starts with ':'. + # + # We don't need to check the current line, since the '&' would + # appear inside the second set of parentheses on the current line as + # opposed to the first set. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 10), -1): + previous_line = clean_lines.elided[i] + if not Search(r'[),]\s*$', previous_line): + break + if Match(r'^\s*:\s+\S', previous_line): + return + + # Avoid preprocessors + if Search(r'\\\s*$', line): + return + + # Avoid constructor initializer lists + if IsInitializerList(clean_lines, linenum): + return + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". Do not check + # those function parameters. + # + # We also accept & in static_assert, which looks like a function but + # it's actually a declaration expression. + whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' + r'operator\s*[<>][<>]|' + r'static_assert|COMPILE_ASSERT' + r')\s*\(') + if Search(whitelisted_functions, line): + return + elif not Search(r'\S+\([^)]*$', line): + # Don't see a whitelisted function on this line. Actually we + # didn't see any function name on this line, so this is likely a + # multi-line parameter list. Try a bit harder to catch this case. + for i in xrange(2): + if (linenum > i and + Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): + return + + decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body + for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): + if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer: ' + + ReplaceAll(' *<', '<', parameter)) + + +def CheckCasts(filename, clean_lines, linenum, error): + """Various cast related checks. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+|\S<\s*(?:const\s+)?)?\b' + r'(int|float|double|bool|char|int32|uint32|int64|uint64)' + r'(\([^)].*)', line) + expecting_function = ExpectingFunctionArgs(clean_lines, linenum) + if match and not expecting_function: + matched_type = match.group(2) + + # matched_new_or_template is used to silence two false positives: + # - New operators + # - Template arguments with function types + # + # For template arguments, we match on types immediately following + # an opening bracket without any spaces. This is a fast way to + # silence the common case where the function type is the first + # template argument. False negative with less-than comparison is + # avoided because those operators are usually followed by a space. + # + # function // bracket + no space = false positive + # value < double(42) // bracket + space = true positive + matched_new_or_template = match.group(1) + + # Avoid arrays by looking for brackets that come after the closing + # parenthesis. + if Match(r'\([^()]+\)\s*\[', match.group(3)): + return + + # Other things to ignore: + # - Function pointers + # - Casts to pointer types + # - Placement new + # - Alias declarations + matched_funcptr = match.group(3) + if (matched_new_or_template is None and + not (matched_funcptr and + (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', + matched_funcptr) or + matched_funcptr.startswith('(*)'))) and + not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and + not Search(r'new\(\S+\)\s*' + matched_type, line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + matched_type) + + if not expecting_function: + CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', + r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', + r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + # + # Some non-identifier character is required before the '&' for the + # expression to be recognized as a cast. These are casts: + # expression = &static_cast(temporary()); + # function(&(int*)(temporary())); + # + # This is not a cast: + # reference_type&(int* function_param); + match = Search( + r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' + r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) + if match: + # Try a better error message when the & is bound to something + # dereferenced by the casted pointer, as opposed to the casted + # pointer itself. + parenthesis_error = False + match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) + if match: + _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) + if x1 >= 0 and clean_lines.elided[y1][x1] == '(': + _, y2, x2 = CloseExpression(clean_lines, y1, x1) + if x2 >= 0: + extended_line = clean_lines.elided[y2][x2:] + if y2 < clean_lines.NumLines() - 1: + extended_line += clean_lines.elided[y2 + 1] + if Match(r'\s*(?:->|\[)', extended_line): + parenthesis_error = True + + if parenthesis_error: + error(filename, linenum, 'readability/casting', 4, + ('Are you taking an address of something dereferenced ' + 'from a cast? Wrapping the dereferenced expression in ' + 'parentheses will make the binding more obvious')) + else: + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + +def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): + """Checks for a C-style cast by looking for the pattern. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + line = clean_lines.elided[linenum] + match = Search(pattern, line) + if not match: + return False + + # Exclude lines with keywords that tend to look like casts + context = line[0:match.start(1) - 1] + if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): + return False + + # Try expanding current context to see if we one level of + # parentheses inside a macro. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 5), -1): + context = clean_lines.elided[i] + context + if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): + return False + + # operator++(int) and operator--(int) + if context.endswith(' operator++') or context.endswith(' operator--'): + return False + + # A single unnamed argument for a function tends to look like old + # style cast. If we see those, don't issue warnings for deprecated + # casts, instead issue warnings for unnamed arguments where + # appropriate. + # + # These are things that we want warnings for, since the style guide + # explicitly require all parameters to be named: + # Function(int); + # Function(int) { + # ConstMember(int) const; + # ConstMember(int) const { + # ExceptionMember(int) throw (...); + # ExceptionMember(int) throw (...) { + # PureVirtual(int) = 0; + # [](int) -> bool { + # + # These are functions of some sort, where the compiler would be fine + # if they had named parameters, but people often omit those + # identifiers to reduce clutter: + # (FunctionPointer)(int); + # (FunctionPointer)(int) = value; + # Function((function_pointer_arg)(int)) + # Function((function_pointer_arg)(int), int param) + # ; + # <(FunctionPointerTemplateArgument)(int)>; + remainder = line[match.end(0):] + if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', + remainder): + # Looks like an unnamed parameter. + + # Don't warn on any kind of template arguments. + if Match(r'^\s*>', remainder): + return False + + # Don't warn on assignments to function pointers, but keep warnings for + # unnamed parameters to pure virtual functions. Note that this pattern + # will also pass on assignments of "0" to function pointers, but the + # preferred values for those would be "nullptr" or "NULL". + matched_zero = Match(r'^\s=\s*(\S+)\s*;', remainder) + if matched_zero and matched_zero.group(1) != '0': + return False + + # Don't warn on function pointer declarations. For this we need + # to check what came before the "(type)" string. + if Match(r'.*\)\s*$', line[0:match.start(0)]): + return False + + # Don't warn if the parameter is named with block comments, e.g.: + # Function(int /*unused_param*/); + raw_line = clean_lines.raw_lines[linenum] + if '/*' in raw_line: + return False + + # Passed all filters, issue warning here. + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return True + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +def ExpectingFunctionArgs(clean_lines, linenum): + """Checks whether where function type arguments are expected. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + + Returns: + True if the line at 'linenum' is inside something that expects arguments + of function types. + """ + line = clean_lines.elided[linenum] + return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + (linenum >= 2 and + (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', + clean_lines.elided[linenum - 1]) or + Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', + clean_lines.elided[linenum - 2]) or + Search(r'\bstd::m?function\s*\<\s*$', + clean_lines.elided[linenum - 1])))) + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator',)), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('tuple',)), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_algorithm_header = [] +for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', + 'transform'): + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '')) + +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + if not filename_cc.endswith('.cc'): + return (False, '') + filename_cc = filename_cc[:-len('.cc')] + if filename_cc.endswith('_unittest'): + filename_cc = filename_cc[:-len('_unittest')] + elif filename_cc.endswith('_test'): + filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_dict, io=codecs): + """Fill up the include_dict with new includes found from the file. + + Args: + filename: the name of the header to read. + include_dict: a dictionary in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was successfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + include_dict.setdefault(include, linenum) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_algorithm_header: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's flatten the include_state include_list and copy it into a dictionary. + include_dict = dict([item for sublist in include_state.include_list + for item in sublist]) + + # Did we find the header for this file (if any) and successfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_dict is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_dict.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_dict, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + + # All the lines have been processed, report the errors found. + for required_header_unstripped in required: + template = required[required_header_unstripped][1] + if required_header_unstripped.strip('<>"') not in include_dict: + error(filename, required[required_header_unstripped][0], + 'build/include_what_you_use', 4, + 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def CheckDefaultLambdaCaptures(filename, clean_lines, linenum, error): + """Check that default lambda captures are not used. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # A lambda introducer specifies a default capture if it starts with "[=" + # or if it starts with "[&" _not_ followed by an identifier. + match = Match(r'^(.*)\[\s*(?:=|&[^\w])', line) + if match: + # Found a potential error, check what comes after the lambda-introducer. + # If it's not open parenthesis (for lambda-declarator) or open brace + # (for compound-statement), it's not a lambda. + line, _, pos = CloseExpression(clean_lines, linenum, len(match.group(1))) + if pos >= 0 and Match(r'^\s*[{(]', line[pos:]): + error(filename, linenum, 'build/c++11', + 4, # 4 = high confidence + 'Default lambda captures are an unapproved C++ feature.') + + +def CheckRedundantVirtual(filename, clean_lines, linenum, error): + """Check if line contains a redundant "virtual" function-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for "virtual" on current line. + line = clean_lines.elided[linenum] + virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) + if not virtual: return + + # Ignore "virtual" keywords that are near access-specifiers. These + # are only used in class base-specifier and do not apply to member + # functions. + if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or + Match(r'^\s+(public|protected|private)\b', virtual.group(3))): + return + + # Ignore the "virtual" keyword from virtual base classes. Usually + # there is a column on the same line in these cases (virtual base + # classes are rare in google3 because multiple inheritance is rare). + if Match(r'^.*[^:]:[^:].*$', line): return + + # Look for the next opening parenthesis. This is the start of the + # parameter list (possibly on the next line shortly after virtual). + # TODO(unknown): doesn't work if there are virtual functions with + # decltype() or other things that use parentheses, but csearch suggests + # that this is rare. + end_col = -1 + end_line = -1 + start_col = len(virtual.group(2)) + for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): + line = clean_lines.elided[start_line][start_col:] + parameter_list = Match(r'^([^(]*)\(', line) + if parameter_list: + # Match parentheses to find the end of the parameter list + (_, end_line, end_col) = CloseExpression( + clean_lines, start_line, start_col + len(parameter_list.group(1))) + break + start_col = 0 + + if end_col < 0: + return # Couldn't find end of parameter list, give up + + # Look for "override" or "final" after the parameter list + # (possibly on the next few lines). + for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): + line = clean_lines.elided[i][end_col:] + match = Search(r'\b(override|final)\b', line) + if match: + error(filename, linenum, 'readability/inheritance', 4, + ('"virtual" is redundant since function is ' + 'already declared as "%s"' % match.group(1))) + + # Set end_col to check whole lines after we are done with the + # first line. + end_col = 0 + if Search(r'[^\w]\s*$', line): + break + + +def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): + """Check if line contains a redundant "override" or "final" virt-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for closing parenthesis nearby. We need one to confirm where + # the declarator ends and where the virt-specifier starts to avoid + # false positives. + line = clean_lines.elided[linenum] + declarator_end = line.rfind(')') + if declarator_end >= 0: + fragment = line[declarator_end:] + else: + if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: + fragment = line + else: + return + + # Check that at most one of "override" or "final" is present, not both + if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): + error(filename, linenum, 'readability/inheritance', 4, + ('"override" is redundant since function is ' + 'already declared as "final"')) + + + + +# Returns true if we are at a new block, and it is directly +# inside of a namespace. +def IsBlockInNameSpace(nesting_state, is_forward_declaration): + """Checks that the new block is directly in a namespace. + + Args: + nesting_state: The _NestingState object that contains info about our state. + is_forward_declaration: If the class is a forward declared class. + Returns: + Whether or not the new block is directly in a namespace. + """ + if is_forward_declaration: + if len(nesting_state.stack) >= 1 and ( + isinstance(nesting_state.stack[-1], _NamespaceInfo)): + return True + else: + return False + + return (len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.stack[-2], _NamespaceInfo)) + + +def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + raw_lines_no_comments, linenum): + """This method determines if we should apply our namespace indentation check. + + Args: + nesting_state: The current nesting state. + is_namespace_indent_item: If we just put a new class on the stack, True. + If the top of the stack is not a class, or we did not recently + add the class, False. + raw_lines_no_comments: The lines without the comments. + linenum: The current line number we are processing. + + Returns: + True if we should apply our namespace indentation check. Currently, it + only works for classes and namespaces inside of a namespace. + """ + + is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, + linenum) + + if not (is_namespace_indent_item or is_forward_declaration): + return False + + # If we are in a macro, we do not want to check the namespace indentation. + if IsMacroDefinition(raw_lines_no_comments, linenum): + return False + + return IsBlockInNameSpace(nesting_state, is_forward_declaration) + + +# Call this method if the line is directly inside of a namespace. +# If the line above is blank (excluding comments) or the start of +# an inner namespace, it cannot be indented. +def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, + error): + line = raw_lines_no_comments[linenum] + if Match(r'^\s+', line): + error(filename, linenum, 'runtime/indentation_namespace', 4, + 'Do not indent within a namespace') + + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error) + if nesting_state.InAsmBlock(): return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + nesting_state, error) + CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckVlogArguments(filename, clean_lines, line, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + CheckDefaultLambdaCaptures(filename, clean_lines, line, error) + CheckRedundantVirtual(filename, clean_lines, line, error) + CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def FlagCxx11Features(filename, clean_lines, linenum, error): + """Flag those c++11 features that we only allow in certain places. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Flag unapproved C++11 headers. + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + if include and include.group(1) in ('cfenv', + 'condition_variable', + 'fenv.h', + 'future', + 'mutex', + 'thread', + 'chrono', + 'ratio', + 'regex', + 'system_error', + ): + error(filename, linenum, 'build/c++11', 5, + ('<%s> is an unapproved C++11 header.') % include.group(1)) + + # The only place where we need to worry about C++11 keywords and library + # features in preprocessor directives is in macro definitions. + if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return + + # These are classes and free functions. The classes are always + # mentioned as std::*, but we only catch the free functions if + # they're not found by ADL. They're alphabetical by header. + for top_name in ( + # type_traits + 'alignment_of', + 'aligned_union', + ): + if Search(r'\bstd::%s\b' % top_name, line): + error(filename, linenum, 'build/c++11', 5, + ('std::%s is an unapproved C++11 class or function. Send c-style ' + 'an example of where it would make your code more readable, and ' + 'they may let you use it.') % top_name) + + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = NestingState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + + if file_extension == 'h': + CheckForHeaderGuard(filename, clean_lines, error) + + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + FlagCxx11Features(filename, clean_lines, line, error) + nesting_state.CheckCompletedBlocks(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # Check that the .cc file has included its header if it exists. + if file_extension == 'cc': + CheckHeaderFileIncluded(filename, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForBadCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessConfigOverrides(filename): + """ Loads the configuration files and processes the config overrides. + + Args: + filename: The name of the file being processed by the linter. + + Returns: + False if the current |filename| should not be processed further. + """ + + abs_filename = os.path.abspath(filename) + cfg_filters = [] + keep_looking = True + while keep_looking: + abs_path, base_name = os.path.split(abs_filename) + if not base_name: + break # Reached the root directory. + + cfg_file = os.path.join(abs_path, "CPPLINT.cfg") + abs_filename = abs_path + if not os.path.isfile(cfg_file): + continue + + try: + with open(cfg_file) as file_handle: + for line in file_handle: + line, _, _ = line.partition('#') # Remove comments. + if not line.strip(): + continue + + name, _, val = line.partition('=') + name = name.strip() + val = val.strip() + if name == 'set noparent': + keep_looking = False + elif name == 'filter': + cfg_filters.append(val) + elif name == 'exclude_files': + # When matching exclude_files pattern, use the base_name of + # the current file name or the directory name we are processing. + # For example, if we are checking for lint errors in /foo/bar/baz.cc + # and we found the .cfg file at /foo/CPPLINT.cfg, then the config + # file's "exclude_files" filter is meant to be checked against "bar" + # and not "baz" nor "bar/baz.cc". + if base_name: + pattern = re.compile(val) + if pattern.match(base_name): + sys.stderr.write('Ignoring "%s": file excluded by "%s". ' + 'File path component "%s" matches ' + 'pattern "%s"\n' % + (filename, cfg_file, base_name, val)) + return False + elif name == 'linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + sys.stderr.write('Line length must be numeric.') + else: + sys.stderr.write( + 'Invalid configuration option (%s) in file %s\n' % + (name, cfg_file)) + + except IOError: + sys.stderr.write( + "Skipping config file '%s': Can't open for reading\n" % cfg_file) + keep_looking = False + + # Apply all the accumulated filters in reverse order (top-level directory + # config options having the least priority). + for filter in reversed(cfg_filters): + _AddFilters(filter) + + return True + + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + _BackupFilters() + + if not ProcessConfigOverrides(filename): + _RestoreFilters() + return + + lf_lines = [] + crlf_lines = [] + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + # Remove trailing '\r'. + # The -1 accounts for the extra trailing blank line we get from split() + for linenum in range(len(lines) - 1): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + crlf_lines.append(linenum + 1) + else: + lf_lines.append(linenum + 1) + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + _RestoreFilters() + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if filename != '-' and file_extension not in _valid_extensions: + sys.stderr.write('Ignoring %s; not a valid file name ' + '(%s)\n' % (filename, ', '.join(_valid_extensions))) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + + # If end-of-line sequences are a mix of LF and CR-LF, issue + # warnings on the lines with CR. + # + # Don't issue any warnings if all lines are uniformly LF or CR-LF, + # since critique can handle these just fine, and the style guide + # doesn't dictate a particular end of line sequence. + # + # We can't depend on os.linesep to determine what the desired + # end-of-line sequence should be, since that will return the + # server-side end-of-line sequence. + if lf_lines and crlf_lines: + # Warn on every line with CR. An alternative approach might be to + # check whether the file is mostly CRLF or just LF, and warn on the + # minority, we bias toward LF here since most tools prefer LF. + for linenum in crlf_lines: + Error(filename, linenum, 'whitespace/newline', 1, + 'Unexpected \\r (^M) found; better to use only \\n') + + sys.stderr.write('Done processing %s\n' % filename) + _RestoreFilters() + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=', + 'root=', + 'linelength=', + 'extensions=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if val not in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--root': + global _root + _root = val + elif opt == '--linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + PrintUsage('Line length must be digits.') + elif opt == '--extensions': + global _valid_extensions + try: + _valid_extensions = set(val.split(',')) + except ValueError: + PrintUsage('Extensions must be comma seperated list.') + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/test.cc b/loader_example.cc similarity index 98% rename from test.cc rename to loader_example.cc index 72b4fa21..d91baaa6 100644 --- a/test.cc +++ b/loader_example.cc @@ -296,7 +296,7 @@ main( char **argv) { if (argc > 1) { - const char* basepath = NULL; + const char* basepath = "models/"; if (argc > 2) { basepath = argv[2]; } @@ -305,7 +305,7 @@ main( //assert(true == TestLoadObj("cornell_box.obj")); //assert(true == TestLoadObj("cube.obj")); assert(true == TestStreamLoadObj()); - assert(true == TestLoadObj("catmark_torus_creases0.obj", NULL, false)); + assert(true == TestLoadObj("models/catmark_torus_creases0.obj", "models/", false)); } return 0; diff --git a/cornell_box.mtl b/models/cornell_box.mtl similarity index 100% rename from cornell_box.mtl rename to models/cornell_box.mtl diff --git a/cornell_box.obj b/models/cornell_box.obj similarity index 100% rename from cornell_box.obj rename to models/cornell_box.obj diff --git a/premake4.lua b/premake4.lua index cbfdfd92..cd5b2952 100644 --- a/premake4.lua +++ b/premake4.lua @@ -1,5 +1,5 @@ sources = { - "test.cc", + "loader_example.cc", } -- premake4.lua @@ -21,9 +21,9 @@ solution "TinyObjLoaderSolution" configuration "Debug" defines { "DEBUG" } -- -DDEBUG flags { "Symbols" } - targetname "test_tinyobjloader_debug" + targetname "loader_example_debug" configuration "Release" -- defines { "NDEBUG" } -- -NDEBUG flags { "Symbols", "Optimize" } - targetname "test_tinyobjloader" + targetname "loader_example" diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..1a1434ab --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,13 @@ +.PHONY: clean + +tester: tester.cc + g++ -g -O0 -o tester tester.cc + +all: tester + +check: tester + ./tester + +clean: + rm -rf tester + diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..6ed65ff9 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,25 @@ +# Build&Test + +## Use makefile + + $ make check + +## Use ninja + kuroga + +Assume + +* ninja 1.4+ +* python 2.6+ + +Are installed. + +### Linux/MacOSX + + $ python kuroga.py config-posix.py + $ ninja + +### Windows + + > python kuroga.py config-msvc.py + > vcbuild.bat + diff --git a/tests/catch.hpp b/tests/catch.hpp new file mode 100644 index 00000000..2a7146a7 --- /dev/null +++ b/tests/catch.hpp @@ -0,0 +1,10445 @@ +/* + * Catch v1.4.0 + * Generated: 2016-03-15 07:23:12.623111 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + +#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// #included from: internal/catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic ignored "-Wglobal-constructors" +# pragma clang diagnostic ignored "-Wvariadic-macros" +# pragma clang diagnostic ignored "-Wc99-extensions" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wc++98-compat" +# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpadded" +#endif +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +#endif + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// #included from: internal/catch_notimplemented_exception.h +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED + +// #included from: catch_common.h +#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include +#include + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? +// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported +// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? +// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? +// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 + +#if defined(__cplusplus) && __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +#endif + +#ifdef __clang__ + +# if __has_feature(cxx_nullptr) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if __has_feature(cxx_noexcept) +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# if defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// + +// Use variadic macros if the compiler supports them +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) + +#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS + +#endif + +// Use __COUNTER__ if the compiler supports it +#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ + ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __clang__ && __clang_major__ >= 3 ) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(CATCH_CPP11_OR_GREATER) + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# endif + +# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) +# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) +# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +# endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NULLPTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_IS_ENUM +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TUPLE +#endif +#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) +# define CATCH_CONFIG_VARIADIC_MACROS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_LONG_LONG +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_UNIQUE_PTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CATCH_CONFIG_CPP11_NULLPTR +# define CATCH_NULL nullptr +#else +# define CATCH_NULL NULL +#endif + +// override support +#ifdef CATCH_CONFIG_CPP11_OVERRIDE +# define CATCH_OVERRIDE override +#else +# define CATCH_OVERRIDE +#endif + +// unique_ptr support +#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR +# define CATCH_AUTO_PTR( T ) std::unique_ptr +#else +# define CATCH_AUTO_PTR( T ) std::auto_ptr +#endif + +namespace Catch { + + struct IConfig; + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; +#else + NonCopyable( NonCopyable const& info ); + NonCopyable& operator = ( NonCopyable const& ); +#endif + + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; + + class SafeBool { + public: + typedef void (SafeBool::*type)() const; + + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; + } + + bool startsWith( std::string const& s, std::string const& prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; + + struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); + SourceLineInfo( SourceLineInfo const& other ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + bool operator < ( SourceLineInfo const& other ) const; + + std::string file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); + +#include + +namespace Catch { + + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + NotImplementedException( NotImplementedException const& ) {} + + virtual ~NotImplementedException() CATCH_NOEXCEPT {} + + virtual const char* what() const CATCH_NOEXCEPT; + + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include + +namespace Catch { + + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; + + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; + + IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr { + public: + Ptr() : m_p( CATCH_NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = CATCH_NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; + + template + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } + + mutable unsigned int m_rc; + }; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +namespace Catch { + + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); + +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include + +namespace Catch { + + class TestSpec; + + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +namespace Catch { + +template +class MethodTestCase : public SharedImpl { + +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void(*TestFunction)(); + +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} + + const char* name; + const char* description; +}; + +void registerTestCase + ( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + +struct AutoReg { + + AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template + AutoReg + ( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + registerTestCase + ( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + +void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + namespace{ \ + struct TestName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + +#else + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + namespace{ \ + struct TestCaseName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void TestCaseName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_result_builder.h +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED + +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include + +namespace Catch { + + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + + struct AssertionResultData + { + AssertionResultData() : resultType( ResultWas::Unknown ) {} + + std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// #included from: catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + namespace Generic { + template class AllOf; + template class AnyOf; + template class Not; + } + + template + struct Matcher : SharedImpl + { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( ExpressionT const& expr ) const = 0; + virtual std::string toString() const = 0; + + Generic::AllOf operator && ( Matcher const& other ) const; + Generic::AnyOf operator || ( Matcher const& other ) const; + Generic::Not operator ! () const; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + template + class Not : public MatcherImpl, ExpressionT> { + public: + explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} + Not( Not const& other ) : m_matcher( other.m_matcher ) {} + + virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { + return !m_matcher->match( expr ); + } + + virtual std::string toString() const CATCH_OVERRIDE { + return "not " + m_matcher->toString(); + } + private: + Ptr< Matcher > m_matcher; + }; + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AllOf operator && ( Matcher const& other ) const { + AllOf allOfExpr( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AnyOf operator || ( Matcher const& other ) const { + AnyOf anyOfExpr( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + } // namespace Generic + + template + Generic::AllOf Matcher::operator && ( Matcher const& other ) const { + Generic::AllOf allOfExpr; + allOfExpr.add( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + template + Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { + Generic::AnyOf anyOfExpr; + anyOfExpr.add( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + template + Generic::Not Matcher::operator ! () const { + return Generic::Not( *this ); + } + + namespace StdString { + + inline std::string makeString( std::string const& str ) { return str; } + inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + + } + std::string toStringSuffix() const + { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : ""; + } + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct Equals : MatcherImpl { + Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( str, caseSensitivity ) + {} + Equals( Equals const& other ) : m_data( other.m_data ){} + + virtual ~Equals(); + + virtual bool match( std::string const& expr ) const { + return m_data.m_str == m_data.adjustString( expr );; + } + virtual std::string toString() const { + return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct Contains : MatcherImpl { + Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + Contains( Contains const& other ) : m_data( other.m_data ){} + + virtual ~Contains(); + + virtual bool match( std::string const& expr ) const { + return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct StartsWith : MatcherImpl { + StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + + StartsWith( StartsWith const& other ) : m_data( other.m_data ){} + + virtual ~StartsWith(); + + virtual bool match( std::string const& expr ) const { + return startsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct EndsWith : MatcherImpl { + EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + EndsWith( EndsWith const& other ) : m_data( other.m_data ){} + + virtual ~EndsWith(); + + virtual bool match( std::string const& expr ) const { + return endsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::Not Not( Impl::Matcher const& m ) { + return Impl::Generic::Not( m ); + } + + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + + inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( str, caseSensitivity ); + } + inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); + } + inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( substr, caseSensitivity ); + } + inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); + } + inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { + return Impl::StdString::StartsWith( substr ); + } + inline Impl::StdString::StartsWith StartsWith( const char* substr ) { + return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { + return Impl::StdString::EndsWith( substr ); + } + inline Impl::StdString::EndsWith EndsWith( const char* substr ) { + return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +namespace Catch { + + struct TestFailureException{}; + + template class ExpressionLhs; + + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct CopyableStream { + CopyableStream() {} + CopyableStream( CopyableStream const& other ) { + oss << other.oss.str(); + } + CopyableStream& operator=( CopyableStream const& other ) { + oss.str(""); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; + }; + + class ResultBuilder { + public: + ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg = "" ); + + template + ExpressionLhs operator <= ( T const& operand ); + ExpressionLhs operator <= ( bool value ); + + template + ResultBuilder& operator << ( T const& value ) { + m_stream.oss << value; + return *this; + } + + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + + ResultBuilder& setResultType( ResultWas::OfType result ); + ResultBuilder& setResultType( bool result ); + ResultBuilder& setLhs( std::string const& lhs ); + ResultBuilder& setRhs( std::string const& rhs ); + ResultBuilder& setOp( std::string const& op ); + + void endExpression(); + + std::string reconstructExpression() const; + AssertionResult build() const; + + void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); + void captureResult( ResultWas::OfType resultType ); + void captureExpression(); + void captureExpectedException( std::string const& expectedMessage ); + void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void handleResult( AssertionResult const& result ); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; + + private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + struct ExprComponents { + ExprComponents() : testFalse( false ) {} + bool testFalse; + std::string lhs, rhs, op; + } m_exprComponents; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; + }; + +} // namespace Catch + +// Include after due to circular dependency: +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#endif + +#include + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; + + template + inline T& opCast(T const& t) { return const_cast(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator{}; + + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return bool( opCast( lhs ) == opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) != opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) < opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) > opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) >= opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) <= opCast( rhs ) ); + } + }; + + template + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // unsigned X to int + template bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // unsigned X to long + template bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // int to unsigned X + template bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // long to unsigned X + template bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long (when comparing against NULL) + template bool compare( long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + + // pointer to int (when comparing against NULL) + template bool compare( int lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, int rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + // long long to unsigned X + template bool compare( long long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // unsigned long long to X + template bool compare( unsigned long long lhs, int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long long (when comparing against NULL) + template bool compare( long long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } +#endif // CATCH_CONFIG_CPP11_LONG_LONG + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template bool compare( std::nullptr_t, T* rhs ) { + return Evaluator::evaluate( nullptr, rhs ); + } + template bool compare( T* lhs, std::nullptr_t ) { + return Evaluator::evaluate( lhs, nullptr ); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +#ifdef CATCH_CONFIG_CPP11_TUPLE +#include +#endif + +#ifdef CATCH_CONFIG_CPP11_IS_ENUM +#include +#endif + +namespace Catch { + +// Why we're here. +template +std::string toString( T const& value ); + +// Built in overloads + +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( const wchar_t* const value ); +std::string toString( wchar_t* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( const float value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ); +std::string toString( unsigned long long value ); +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ); +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); +#endif + +namespace Detail { + + extern const std::string unprintableString; + + struct BorgType { + template BorgType( T const& ); + }; + + struct TrueType { char sizer[1]; }; + struct FalseType { char sizer[2]; }; + + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; + +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template::value + > + struct EnumStringMaker + { + static std::string convert( T const& ) { return unprintableString; } + }; + + template + struct EnumStringMaker + { + static std::string convert( T const& v ) + { + return ::Catch::toString( + static_cast::type>(v) + ); + } + }; +#endif + template + struct StringMakerBase { +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template + static std::string convert( T const& v ) + { + return EnumStringMaker::convert( v ); + } +#else + template + static std::string convert( T const& ) { return unprintableString; } +#endif + }; + + template<> + struct StringMakerBase { + template + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + inline std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + +} // end namespace Detail + +template +struct StringMaker : + Detail::StringMakerBase::value> {}; + +template +struct StringMaker { + template + static std::string convert( U* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +template +struct StringMaker { + static std::string convert( R C::* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ); +} + +//template +//struct StringMaker > { +// static std::string convert( std::vector const& v ) { +// return Detail::rangeToString( v.begin(), v.end() ); +// } +//}; + +template +std::string toString( std::vector const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); +} + +#ifdef CATCH_CONFIG_CPP11_TUPLE + +// toString for tuples +namespace TupleDetail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct ElementPrinter { + static void print( const Tuple& tuple, std::ostream& os ) + { + os << ( N ? ", " : " " ) + << Catch::toString(std::get(tuple)); + ElementPrinter::print(tuple,os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct ElementPrinter { + static void print( const Tuple&, std::ostream& ) {} + }; + +} + +template +struct StringMaker> { + + static std::string convert( const std::tuple& tuple ) + { + std::ostringstream os; + os << '{'; + TupleDetail::ElementPrinter>::print( tuple, os ); + os << " }"; + return os.str(); + } +}; +#endif // CATCH_CONFIG_CPP11_TUPLE + +namespace Detail { + template + std::string makeString( T const& value ) { + return StringMaker::convert( value ); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template +std::string toString( T const& value ) { + return StringMaker::convert( value ); +} + + namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << Catch::toString( *first ); + for( ++first ; first != last ; ++first ) + oss << ", " << Catch::toString( *first ); + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +namespace Catch { + +// Wraps the LHS of an expression and captures the operator and RHS (if any) - +// wrapping them all in a ResultBuilder object +template +class ExpressionLhs { + ExpressionLhs& operator = ( ExpressionLhs const& ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs& operator = ( ExpressionLhs && ) = delete; +# endif + +public: + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs( ExpressionLhs const& ) = default; + ExpressionLhs( ExpressionLhs && ) = default; +# endif + + template + ResultBuilder& operator == ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator != ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator < ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator > ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator <= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator >= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator == ( bool rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator != ( bool rhs ) { + return captureExpression( rhs ); + } + + void endExpression() { + bool value = m_lhs ? true : false; + m_rb + .setLhs( Catch::toString( value ) ) + .setResultType( value ) + .endExpression(); + } + + // Only simple binary expressions are allowed on the LHS. + // If more complex compositions are required then place the sub expression in parentheses + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + +private: + template + ResultBuilder& captureExpression( RhsT const& rhs ) { + return m_rb + .setResultType( Internal::compare( m_lhs, rhs ) ) + .setLhs( Catch::toString( m_lhs ) ) + .setRhs( Catch::toString( rhs ) ) + .setOp( Internal::OperatorTraits::getName() ); + } + +private: + ResultBuilder& m_rb; + T m_lhs; +}; + +} // end namespace Catch + + +namespace Catch { + + template + inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + return ExpressionLhs( *this, operand ); + } + + inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { + return ExpressionLhs( *this, value ); + } + +} // namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; + }; + + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage const& other ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + }; + + IResultCapture& getResultCapture(); +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_IPHONE +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CATCH_PLATFORM_WINDOWS +#endif + +#include + +namespace Catch{ + + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} + +#ifdef CATCH_PLATFORM_MAC + + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #ifdef DEBUG + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_BREAK_INTO_DEBUGGER() \ + if( Catch::isDebuggerActive() ) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } + #else + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #endif + #endif + +#elif defined(_MSC_VER) + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#endif + +// #included from: catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { + class TestCase; + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + __catchResult.captureExpectedException( matcher ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( log, macroName ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + try { \ + std::string matcherAsString = (matcher).toString(); \ + __catchResult \ + .setLhs( Catch::toString( arg ) ) \ + .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ + .setOp( "matches" ) \ + .setResultType( (matcher).match( arg ) ); \ + __catchResult.captureExpression(); \ + } catch( ... ) { \ + __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool allOk() const { + return failed == 0; + } + + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description = std::string() ); + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include +#endif + +namespace Catch { + + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedMicroseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + + private: + uint64_t m_ticks; + }; + +} // namespace Catch + +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#else + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +template +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; +}; + +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + + virtual T getValue( std::size_t index ) const { + return m_from+static_cast( index ); + } + + virtual std::size_t size() const { + return static_cast( 1+m_to-m_from ); + } + +private: + + T m_from; + T m_to; +}; + +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator(){} + + void add( T value ) { + m_values.push_back( value ); + } + + virtual T getValue( std::size_t index ) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector m_values; +}; + +template +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) + { + move( other ); + } + + CompositeGenerator& setFileInfo( const char* fileInfo ) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll( m_composed ); + } + + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); + + typename std::vector*>::const_iterator it = m_composed.begin(); + typename std::vector*>::const_iterator itEnd = m_composed.end(); + for( size_t index = 0; it != itEnd; ++it ) + { + const IGenerator* generator = *it; + if( overallIndex >= index && overallIndex < index + generator->size() ) + { + return generator->getValue( overallIndex-index ); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add( const IGenerator* generator ) { + m_totalSize += generator->size(); + m_composed.push_back( generator ); + } + + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); + return *this; + } + + CompositeGenerator& then( T value ) { + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( value ); + add( valuesGen ); + return *this; + } + +private: + + void move( CompositeGenerator& other ) { + std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators +{ + template + CompositeGenerator between( T from, T to ) { + CompositeGenerator generators; + generators.add( new BetweenGenerator( from, to ) ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3 ){ + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) + +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include +#include + +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; + virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +namespace Catch { + + typedef std::string(*exceptionTranslateFunction)(); + + struct IExceptionTranslator; + typedef std::vector ExceptionTranslators; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + try { + if( it == itEnd ) + throw; + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_scale( 1.0 ), + m_value( value ) + {} + + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} + + static Approx custom() { + return Approx( 0 ); + } + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.scale( m_scale ); + return approx; + } + + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template<> +inline std::string toString( Detail::Approx const& value ) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_interfaces_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED + +// #included from: catch_tag_alias.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED + +#include + +namespace Catch { + + struct TagAlias { + TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + + std::string tag; + SourceLineInfo lineInfo; + }; + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( CATCH_NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = CATCH_NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } + + bool operator !() const { return nullableValue == CATCH_NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +namespace Catch { + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public SharedImpl { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( CATCH_NULL, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template + struct StringHolder : MatcherImpl{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: " + Catch::toString( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: " + Catch::toString( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: " + Catch::toString( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: " + Catch::toString( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) + +#endif + +#ifdef CATCH_IMPL +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: ../catch_session.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec_parser.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_wildcard_pattern.hpp +#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_wildcard( NoWildcard ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, "*" ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, "*" ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + virtual ~WildcardPattern(); + virtual bool matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + throw std::logic_error( "Unknown enum" ); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + private: + std::string adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard; + std::string m_pattern; + }; +} + +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); + } + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + private: + Ptr m_underlyingPattern; + }; + + struct Filter { + std::vector > m_patterns; + + bool matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + if( !(*it)->matches( testCase ) ) + return false; + return true; + } + }; + + public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) + if( it->matches( testCase ) ) + return true; + return false; + } + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + private: + void visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + } + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + template + void addPattern() { + std::string token = subString(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + Ptr pattern = new T( token ); + if( m_exclusion ) + pattern = new TestSpec::ExcludedPattern( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + }; + inline TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct Verbosity { enum Level { + NoOutput = 0, + Quiet, + Normal + }; }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + + class TestSpec; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + }; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; +} + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + + struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; + }; + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( std::string const& filename ); + virtual ~FileStream() CATCH_NOEXCEPT; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); + virtual ~CoutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class DebugOutStream : public IStream { + std::auto_ptr m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream(); + virtual ~DebugOutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; +} + +#include +#include +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + filenamesAsTags( false ), + abortAfter( -1 ), + rngSeed( 0 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ), + runOrder( RunTests::InDeclarationOrder ), + useColour( UseColour::Auto ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool filenamesAsTags; + + int abortAfter; + unsigned int rngSeed; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; + + std::string outputFilename; + std::string name; + std::string processName; + + std::vector reporterNames; + std::vector testsOrTags; + }; + + class Config : public SharedImpl { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) + parser.parse( data.testsOrTags[i] ); + m_testSpec = parser.testSpec(); + } + } + + virtual ~Config() { + } + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { return m_data.processName; } + + bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + + std::vector getReporterNames() const { return m_data.reporterNames; } + + int abortAfter() const { return m_data.abortAfter; } + + TestSpec const& testSpec() const { return m_testSpec; } + + bool showHelp() const { return m_data.showHelp; } + bool showInvisibles() const { return m_data.showInvisibles; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_stream->stream(); } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } + virtual unsigned int rngSeed() const { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + + private: + + IStream const* openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + } + else + return new FileStream( m_data.outputFilename ); + } + ConfigData m_data; + + std::auto_ptr m_stream; + TestSpec m_testSpec; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Version 0.0.1.1 + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +# define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +# define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +# define CLARA_AUTO_PTR( T ) std::unique_ptr +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + // Use this to try and stop compiler from warning about unreachable code + inline bool isTrue( bool value ) { return value; } + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + + template struct IsBool { static const bool value = false; }; + template<> struct IsBool { static const bool value = true; }; + + template + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } + inline void convertInto( bool _source, bool& _dest ) { + _dest = _source; + } + template + inline void convertInto( bool, T& ) { + if( isTrue( true ) ) + throw std::runtime_error( "Invalid conversion" ); + } + + template + struct IArgFunction { + virtual ~IArgFunction() {} +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; +#endif + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual void setFlag( ConfigT& config ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( CLARA_NULL ) {} + BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + void setFlag( ConfigT& config ) const { + functionObj->setFlag( config ); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != CLARA_NULL; + } + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction{ + virtual void set( C&, std::string const& ) const {} + virtual void setFlag( C& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder( *this ); } + }; + + template + struct BoundDataMember : IArgFunction{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual void setFlag( C& p ) const { + convertInto( true, p.*member ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template + struct BoundUnaryMethod : IArgFunction{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual void setFlag( C& p ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template + struct BoundNullaryMethod : IArgFunction{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual void setFlag( C& p ) const { + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual void setFlag( C& p ) const { + function( p ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template + struct BoundBinaryFunction : IArgFunction{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual void setFlag( C& obj ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + } // namespace Detail + + struct Parser { + Parser() : separators( " \t=:" ) {} + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + void parseIntoTokens( int argc, char const* const argv[], std::vector& tokens ) const { + const std::string doubleDash = "--"; + for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) + parseIntoTokens( argv[i] , tokens); + } + void parseIntoTokens( std::string arg, std::vector& tokens ) const { + while( !arg.empty() ) { + Parser::Token token( Parser::Token::Positional, arg ); + arg = ""; + if( token.data[0] == '-' ) { + if( token.data.size() > 1 && token.data[1] == '-' ) { + token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); + } + else { + token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); + if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { + arg = "-" + token.data.substr( 1 ); + token.data = token.data.substr( 0, 1 ); + } + } + } + if( token.type != Parser::Token::Positional ) { + std::size_t pos = token.data.find_first_of( separators ); + if( pos != std::string::npos ) { + arg = token.data.substr( pos+1 ); + token.data = token.data.substr( 0, pos ); + } + } + tokens.push_back( token ); + } + } + std::string separators; + }; + + template + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template + class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_floatingArg.get() ) + m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg.reset( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember( field ); + } + template + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage( oss ); + return oss.str(); + } + + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_floatingArg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); + } + + void usage( std::ostream& os, std::string const& procName ) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; + } + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); + } + + ConfigT parse( int argc, char const* const argv[] ) const { + ConfigT config; + parseInto( argc, argv, config ); + return config; + } + + std::vector parseInto( int argc, char const* argv[], ConfigT& config ) const { + std::string processName = argv[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector tokens; + Parser parser; + parser.parseIntoTokens( argc, argv, tokens ); + return populate( tokens, config ); + } + + std::vector populate( std::vector const& tokens, ConfigT& config ) const { + validate(); + std::vector unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } + + std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + std::vector errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; + + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.setFlag( config ); + } + break; + } + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + } + } + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( errors.empty() && m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); + } + return unusedTokens; + } + std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { + if( !m_floatingArg.get() ) + return tokens; + std::vector unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_floatingArg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + void validate() const + { + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); + } + + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include + +namespace Catch { + + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; + } + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + } + inline void setOrder( ConfigData& config, std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + } + inline void setRngSeed( ConfigData& config, std::string const& seed ) { + if( seed == "time" ) { + config.rngSeed = static_cast( std::time(0) ); + } + else { + std::stringstream ss; + ss << seed; + ss >> config.rngSeed; + if( ss.fail() ) + throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + } + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = static_cast( level ); + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void setUseColour( ConfigData& config, std::string const& value ) { + std::string mode = toLower( value ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); + } + inline void forceColour( ConfigData& config ) { + config.useColour = UseColour::Yes; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, "#" ) ) + addTestOrTags( config, "\"" + line + "\"," ); + } + } + + inline Clara::CommandLine makeCommandLineParser() { + + using namespace Clara; + CommandLine cli; + + cli.bindProcessName( &ConfigData::processName ); + + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .bind( &ConfigData::showHelp ); + + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); + + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); + + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .bind( &ConfigData::showSuccessfulTests ); + + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .bind( &ConfigData::shouldDebugBreak ); + + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .bind( &ConfigData::noThrow ); + + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); + + cli["-o"]["--out"] + .describe( "output filename" ) + .bind( &ConfigData::outputFilename, "filename" ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &addReporterName, "name" ); + + cli["-n"]["--name"] + .describe( "suite name" ) + .bind( &ConfigData::name, "name" ); + + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .bind( &abortAfterFirst ); + + cli["-x"]["--abortx"] + .describe( "abort after x failures" ) + .bind( &abortAfterX, "no. failures" ); + + cli["-w"]["--warn"] + .describe( "enable warnings" ) + .bind( &addWarning, "warning name" ); + +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); + + cli[_] + .describe( "which test or tests to use" ) + .bind( &addTestOrTags, "test name, pattern or tags" ); + + cli["-d"]["--durations"] + .describe( "show test durations" ) + .bind( &setShowDurations, "yes|no" ); + + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); + + cli["-#"]["--filenames-as-tags"] + .describe( "adds a tag for the filename" ) + .bind( &ConfigData::filenamesAsTags ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); + + cli["--order"] + .describe( "test case order (defaults to decl)" ) + .bind( &setOrder, "decl|lex|rand" ); + + cli["--rng-seed"] + .describe( "set a specific seed for random numbers" ) + .bind( &setRngSeed, "'time'|number" ); + + cli["--force-colour"] + .describe( "force colourised output (deprecated)" ) + .bind( &forceColour ); + + cli["--use-colour"] + .describe( "should output be colourised" ) + .bind( &setUseColour, "yes|no" ); + + return cli; + } + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour const& other ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved; + }; + + inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include +#include + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template + struct LazyStat : Option { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); + +# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + }; + + struct IReporterFactory : IShared { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map > FactoryMap; + typedef std::vector > Listeners; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); + +} + +#include +#include + +namespace Catch { + + inline std::size_t listTests( Config const& config ) { + + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + else + Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + return matchedTests; + } + + inline std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Catch::cout() << testCaseInfo.name << std::endl; + } + return matchedTests; + } + + struct TagInfo { + TagInfo() : count ( 0 ) {} + void add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + std::string all() const { + std::string out; + for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it ) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; + }; + + inline std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower( tagName ); + std::map::iterator countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper( countIt->second.all(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + Catch::cout() << oss.str() << wrapper << "\n"; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + return tagCounts.size(); + } + + inline std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + Catch::cout() << " " + << it->first + << ":" + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + inline Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch + +// #included from: internal/catch_run_context.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct ITracker : SharedImpl<> { + virtual ~ITracker(); + + // static queries + virtual std::string name() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( Ptr const& child ) = 0; + virtual ITracker* findChild( std::string const& name ) = 0; + virtual void openChild() = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + Ptr m_rootTracker; + ITracker* m_currentTracker; + RunState m_runState; + + public: + + static TrackerContext& instance() { + static TrackerContext s_instance; + return s_instance; + } + + TrackerContext() + : m_currentTracker( CATCH_NULL ), + m_runState( NotStarted ) + {} + + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; + } + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void completeCycle() { + m_runState = CompletedCycle; + } + + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + class TrackerHasName { + std::string m_name; + public: + TrackerHasName( std::string const& name ) : m_name( name ) {} + bool operator ()( Ptr const& tracker ) { + return tracker->name() == m_name; + } + }; + typedef std::vector > Children; + std::string m_name; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; + public: + TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : m_name( name ), + m_ctx( ctx ), + m_parent( parent ), + m_runState( NotStarted ) + {} + virtual ~TrackerBase(); + + virtual std::string name() const CATCH_OVERRIDE { + return m_name; + } + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; + } + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); + } + virtual bool hasChildren() const CATCH_OVERRIDE { + return !m_children.empty(); + } + + virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { + m_children.push_back( child ); + } + + virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + return( it != m_children.end() ) + ? it->get() + : CATCH_NULL; + } + virtual ITracker& parent() CATCH_OVERRIDE { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + virtual void openChild() CATCH_OVERRIDE { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + void open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + virtual void close() CATCH_OVERRIDE { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NotStarted: + case CompletedSuccessfully: + case Failed: + throw std::logic_error( "Illogical state" ); + + case NeedsAnotherRun: + break;; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + default: + throw std::logic_error( "Unexpected state" ); + } + moveToParent(); + m_ctx.completeCycle(); + } + virtual void fail() CATCH_OVERRIDE { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { + m_runState = NeedsAnotherRun; + } + private: + void moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void moveToThis() { + m_ctx.setCurrentTracker( this ); + } + }; + + class SectionTracker : public TrackerBase { + public: + SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( name, ctx, parent ) + {} + virtual ~SectionTracker(); + + static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + SectionTracker* section = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( name ) ) { + section = dynamic_cast( childTracker ); + assert( section ); + } + else { + section = new SectionTracker( name, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() && !section->isComplete() ) { + + section->open(); + } + return *section; + } + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index; + public: + IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( name, ctx, parent ), + m_size( size ), + m_index( -1 ) + {} + virtual ~IndexTracker(); + + static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + IndexTracker* tracker = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( name ) ) { + tracker = dynamic_cast( childTracker ); + assert( tracker ); + } + else { + tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int index() const { return m_index; } + + void moveNext() { + m_index++; + m_children.clear(); + } + + virtual void close() CATCH_OVERRIDE { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + }; + + inline ITracker& TrackerContext::startRun() { + m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_currentTracker = CATCH_NULL; + m_runState = Executing; + return *m_rootTracker; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// #included from: catch_fatal_condition.hpp +#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED + +namespace Catch { + + // Report the error condition then exit the process + inline void fatal( std::string const& message, int exitCode ) { + IContext& context = Catch::getCurrentContext(); + IResultCapture* resultCapture = context.getResultCapture(); + resultCapture->handleFatalErrorCondition( message ); + + if( Catch::alwaysTrue() ) // avoids "no return" warnings + exit( exitCode ); + } + +} // namespace Catch + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { + + struct FatalConditionHandler { + void reset() {} + }; + +} // namespace Catch + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include + +namespace Catch { + + struct SignalDefs { int id; const char* name; }; + extern SignalDefs signalDefs[]; + SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + struct FatalConditionHandler { + + static void handleSignal( int sig ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + if( sig == signalDefs[i].id ) + fatal( signalDefs[i].name, -sig ); + fatal( "", -sig ); + } + + FatalConditionHandler() : m_isSet( true ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + signal( signalDefs[i].id, handleSignal ); + } + ~FatalConditionHandler() { + reset(); + } + void reset() { + if( m_isSet ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + signal( signalDefs[i].id, SIG_DFL ); + m_isSet = false; + } + } + + bool m_isSet; + }; + +} // namespace Catch + +#endif // not Windows + +#include +#include + +namespace Catch { + + class StreamRedirect { + + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) + { + stream.rdbuf( m_oss.rdbuf() ); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); + } + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr const& _config, Ptr const& reporter ) + : m_runInfo( _config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( CATCH_NULL ), + m_config( _config ), + m_reporter( reporter ) + { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + + do { + m_trackerContext.startRun(); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); + } + // !TBD: deprecated - this will be replaced by indexed trackers + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + + private: // IResultCapture + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastResult = result; + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + std::ostringstream oss; + oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + if( !sectionTracker.isOpen() ) + return false; + m_activeSections.push_back( §ionTracker ); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 ) + return false; + if( !m_config->warnAboutMissingAssertions() ) + return false; + if( m_trackerContext.currentTracker().hasChildren() ) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded( SectionEndInfo const& endInfo ) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( !m_activeSections.empty() ) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { + if( m_unfinishedSections.empty() ) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back( endInfo ); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : ""; + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + virtual void handleFatalErrorCondition( std::string const& message ) { + ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); + resultBuilder.setResultType( ResultWas::FatalErrorCondition ); + resultBuilder << message; + resultBuilder.captureExpression(); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); + m_reporter->sectionEnded( testCaseSectionStats ); + + TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + "", + "", + false ) ); + m_totals.testCases.failed++; + testGroupEnded( "", m_totals, 1, 1 ); + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); + } + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + + seedRng( *m_config ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( Catch::cout(), redirectedCout ); + StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + invokeActiveTestCase(); + } + else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + makeUnexpectedResultBuilder().useActiveException(); + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( testCaseInfo.okToFail() ) { + std::swap( assertions.failedButOk, assertions.failed ); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } + + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + + void invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + private: + + ResultBuilder makeUnexpectedResultBuilder() const { + return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition ); + } + + void handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) + sectionEnded( *it ); + m_unfinishedSections.clear(); + } + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + }; + + IResultCapture& getResultCapture() { + if( IResultCapture* capture = getCurrentContext().getResultCapture() ) + return *capture; + else + throw std::logic_error( "No result capture instance" ); + } + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + std::string const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + + private: + void operator=( Version const& ); + }; + + extern Version libraryVersion; +} + +#include +#include +#include + +namespace Catch { + + Ptr createReporter( std::string const& reporterName, Ptr const& config ) { + Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); + if( !reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); + } + return reporter; + } + + Ptr makeReporter( Ptr const& config ) { + std::vector reporters = config->getReporterNames(); + if( reporters.empty() ) + reporters.push_back( "console" ); + + Ptr reporter; + for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); + it != itEnd; + ++it ) + reporter = addReporter( reporter, createReporter( *it, config ) ); + return reporter; + } + Ptr addListeners( Ptr const& config, Ptr reporters ) { + IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); + for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); + it != itEnd; + ++it ) + reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); + return reporters; + } + + Totals runTests( Ptr const& config ) { + + Ptr iconfig = config.get(); + + Ptr reporter = makeReporter( config ); + reporter = addListeners( iconfig, reporter ); + + RunContext context( iconfig, reporter ); + + Totals totals; + + context.testGroupStarting( config->name(), 1, 1 ); + + TestSpec testSpec = config->testSpec(); + if( !testSpec.hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests + + std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); + for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); + it != itEnd; + ++it ) { + if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) + totals += context.runTest( *it ); + else + reporter->skipTest( *it ); + } + + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); + return totals; + } + + void applyFilenamesAsTags( IConfig const& config ) { + std::vector const& tests = getAllTestCasesSorted( config ); + for(std::size_t i = 0; i < tests.size(); ++i ) { + TestCase& test = const_cast( tests[i] ); + std::set tags = test.tags; + + std::string filename = test.lineInfo.file; + std::string::size_type lastSlash = filename.find_last_of( "\\/" ); + if( lastSlash != std::string::npos ) + filename = filename.substr( lastSlash+1 ); + + std::string::size_type lastDot = filename.find_last_of( "." ); + if( lastDot != std::string::npos ) + filename = filename.substr( 0, lastDot ); + + tags.insert( "#" + filename ); + setTags( test, tags ); + } + } + + class Session : NonCopyable { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + Catch::cerr() << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + + m_cli.usage( Catch::cout(), processName ); + Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( Catch::cout(), m_configData.processName ); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; + m_config.reset(); + } + + int run( int argc, char const* argv[] ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + int run( int argc, char* argv[] ) { + return run( argc, const_cast( argv ) ); + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; + }; + + bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace Catch { + + struct LexSort { + bool operator() (TestCase i,TestCase j) const { return (i sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end(), LexSort() ); + break; + case RunTests::InRandomOrder: + { + seedRng( config ); + + RandomNumberGenerator rng; + std::random_shuffle( sorted.begin(), sorted.end(), rng ); + } + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); + it != itEnd; + ++it ) { + std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); + if( !prev.second ){ + Catch::cerr() + << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + exit(1); + } + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) + if( matchTest( *it, testSpec, config ) ) + filtered.push_back( *it ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() + : m_currentSortOrder( RunTests::InDeclarationOrder ), + m_unnamedCount( 0 ) + {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name == "" ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + virtual std::vector const& getAllTests() const { + return m_functions; + } + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder; + mutable std::vector m_sortedFunctions; + size_t m_unnamedCount; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, "&" ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + + void registerTestCase + ( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + getMutableRegistryHub().registerTest + ( makeTestCase + ( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); + } + + /////////////////////////////////////////////////////////////////////////// + + AutoReg::AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCaseFunction( function, lineInfo, nameAndDesc ); + } + + AutoReg::~AutoReg() {} + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + virtual ~ReporterRegistry() CATCH_OVERRIDE {} + + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return CATCH_NULL; + return it->second->create( ReporterConfig( config ) ); + } + + void registerReporter( std::string const& name, Ptr const& factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + void registerListener( Ptr const& factory ) { + m_listeners.push_back( factory ); + } + + virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { + return m_factories; + } + virtual Listeners const& getListeners() const CATCH_OVERRIDE { + return m_listeners; + } + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } + + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::toString( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + throw; + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string tryTranslators() const { + if( m_translators.empty() ) + throw; + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } + + private: + std::vector m_translators; + }; +} + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerListener( factory ); + } + virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = CATCH_NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = CATCH_NULL; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include + +namespace Catch { + + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } + + const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); + } + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << "'"; + throw std::domain_error( oss.str() ); + } + } + + std::ostream& FileStream::stream() const { + return m_ofs; + } + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + std::ostream& DebugOutStream::stream() const { + return m_os; + } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} + + std::ostream& CoutStream::stream() const { + return m_os; + } + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { + return std::cout; + } + std::ostream& cerr() { + return std::cerr; + } +#endif +} + +namespace Catch { + + class Context : public IMutableContext { + + Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} + Context( Context const& ); + void operator=( Context const& ); + + public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : CATCH_NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; + }; + + namespace { + Context* currentContext = CATCH_NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext() { + delete currentContext; + currentContext = CATCH_NULL; + } +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = !isDebuggerActive() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + IColourImpl* platformColourInstance() { + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } + Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} + + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } + return false; + } + + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() + { + return new GeneratorsForTest(); + } + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} + + AssertionResult::AssertionResult() {} + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + AssertionResult::~AssertionResult() {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructedExpression; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + + inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, "." ) || + tag == "hide" || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else + return TestCaseInfo::None; + } + inline bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + } + inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + if( isReservedTag( tag ) ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard( Colour::FileName ); + Catch::cerr() << _lineInfo << std::endl; + } + exit(1); + } + } + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { + char c = _descOrTags[i]; + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( prop == TestCaseInfo::IsHidden ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.insert( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) + { + testCaseInfo.tags = tags; + testCaseInfo.lcaseTags.clear(); + + std::ostringstream oss; + for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { + oss << "[" << *it << "]"; + std::string lcaseTag = toLower( *it ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.insert( lcaseTag ); + } + testCaseInfo.tagsAsString = oss.str(); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + lcaseTags( other.lcaseTags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + properties( other.properties ) + {} + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + name.swap( other.name ); + className.swap( other.className ); + description.swap( other.description ); + tags.swap( other.tags ); + lcaseTags.swap( other.lcaseTags ); + tagsAsString.swap( other.tagsAsString ); + std::swap( TestCaseInfo::properties, static_cast( other ).properties ); + std::swap( lineInfo, other.lineInfo ); + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << "." + << version.minorVersion << "." + << version.patchNumber; + + if( !version.branchName.empty() ) { + os << "-" << version.branchName + << "." << version.buildNumber; + } + return os; + } + + Version libraryVersion( 1, 4, 0, "", 0 ); + +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::ScopedMessage( ScopedMessage const& other ) + : m_info( other.m_info ) + {} + + ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage( m_info ); + } + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch +{ + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + + class LegacyReporterAdapter : public SharedImpl + { + public: + LegacyReporterAdapter( Ptr const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + virtual void skipTest( TestCaseInfo const& ); + + private: + Ptr m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); + rb << it->message; + rb.setResultType( ResultWas::Info ); + AssertionResult result = rb.build(); + m_legacyReporter->Result( result ); + } + } + } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } + void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { + } +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace Catch { + + namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency( reinterpret_cast( &hz ) ); + QueryPerformanceCounter( reinterpret_cast( &hzo ) ); + } + uint64_t t; + QueryPerformanceCounter( reinterpret_cast( &t ) ); + return ((t-hzo)*1000000)/hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,CATCH_NULL); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + } +#endif + } + + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return static_cast(getElapsedMicroseconds()/1000); + } + double Timer::getElapsedSeconds() const { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << "s"; + return os; + } + + SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) + : file( other.file ), + line( other.line ) + {} + bool SourceLineInfo::empty() const { + return file.empty(); + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && file == other.file; + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { + return line < other.line || ( line == other.line && file < other.file ); + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + os << info.file << ":" << info.line; +#endif + return os; + } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << "'"; + if( alwaysTrue() ) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include + +#ifdef CATCH_PLATFORM_MAC + + #include + #include + #include + #include + #include + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) + { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); + ++i; + } + } + } + return "\"" + s + "\""; +} +std::string toString( std::wstring const& value ) { + + std::string s; + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; + return Catch::toString( s ); +} + +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +std::string toString( char* const value ) { + return Catch::toString( static_cast( value ) ); +} + +std::string toString( const wchar_t* const value ) +{ + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); +} + +std::string toString( wchar_t* const value ) +{ + return Catch::toString( static_cast( value ) ); +} + +std::string toString( int value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString( unsigned long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString( unsigned int value ) { + return Catch::toString( static_cast( value ) ); +} + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +std::string toString( const double value ) { + return fpToString( value, 10 ); +} +std::string toString( const float value ) { + return fpToString( value, 5 ) + "f"; +} + +std::string toString( bool value ) { + return value ? "true" : "false"; +} + +std::string toString( char value ) { + return value < ' ' + ? toString( static_cast( value ) ) + : Detail::makeString( value ); +} + +std::string toString( signed char value ) { + return toString( static_cast( value ) ); +} + +std::string toString( unsigned char value ) { + return toString( static_cast( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +std::string toString( unsigned long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + +} // end namespace Catch + +// #included from: catch_result_builder.hpp +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED + +namespace Catch { + + std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { + return secondArg.empty() || secondArg == "\"\"" + ? capturedExpression + : capturedExpression + ", " + secondArg; + } + ResultBuilder::ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg ) + : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + m_shouldDebugBreak( false ), + m_shouldThrow( false ) + {} + + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ResultBuilder& ResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } + ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { + m_exprComponents.lhs = lhs; + return *this; + } + ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { + m_exprComponents.rhs = rhs; + return *this; + } + ResultBuilder& ResultBuilder::setOp( std::string const& op ) { + m_exprComponents.op = op; + return *this; + } + + void ResultBuilder::endExpression() { + m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); + captureExpression(); + } + + void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult( ResultWas::ThrewException ); + } + + void ResultBuilder::captureResult( ResultWas::OfType resultType ) { + setResultType( resultType ); + captureExpression(); + } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { + if( expectedMessage.empty() ) + captureExpectedException( Matchers::Impl::Generic::AllOf() ); + else + captureExpectedException( Matchers::Equals( expectedMessage ) ); + } + + void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + + assert( m_exprComponents.testFalse == false ); + AssertionResultData data = m_data; + data.resultType = ResultWas::Ok; + data.reconstructedExpression = m_assertionInfo.capturedExpression; + + std::string actualMessage = Catch::translateActiveException(); + if( !matcher.match( actualMessage ) ) { + data.resultType = ResultWas::ExpressionFailed; + data.reconstructedExpression = actualMessage; + } + AssertionResult result( m_assertionInfo, data ); + handleResult( result ); + } + + void ResultBuilder::captureExpression() { + AssertionResult result = build(); + handleResult( result ); + } + void ResultBuilder::handleResult( AssertionResult const& result ) + { + getResultCapture().assertionEnded( result ); + + if( !result.isOk() ) { + if( getCurrentContext().getConfig()->shouldDebugBreak() ) + m_shouldDebugBreak = true; + if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) + m_shouldThrow = true; + } + } + void ResultBuilder::react() { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } + bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + + AssertionResult ResultBuilder::build() const + { + assert( m_data.resultType != ResultWas::Unknown ); + + AssertionResultData data = m_data; + + // Flip bool results if testFalse is set + if( m_exprComponents.testFalse ) { + if( data.resultType == ResultWas::Ok ) + data.resultType = ResultWas::ExpressionFailed; + else if( data.resultType == ResultWas::ExpressionFailed ) + data.resultType = ResultWas::Ok; + } + + data.message = m_stream.oss.str(); + data.reconstructedExpression = reconstructExpression(); + if( m_exprComponents.testFalse ) { + if( m_exprComponents.op == "" ) + data.reconstructedExpression = "!" + data.reconstructedExpression; + else + data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + } + return AssertionResult( m_assertionInfo, data ); + } + std::string ResultBuilder::reconstructExpression() const { + if( m_exprComponents.op == "" ) + return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + else if( m_exprComponents.op == "matches" ) + return m_exprComponents.lhs + " " + m_exprComponents.rhs; + else if( m_exprComponents.op != "!" ) { + if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && + m_exprComponents.lhs.find("\n") == std::string::npos && + m_exprComponents.rhs.find("\n") == std::string::npos ) + return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; + else + return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + } + else + return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + } + +} // end namespace Catch + +// #included from: catch_tag_alias_registry.hpp +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + static TagAliasRegistry& get(); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +#include +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + Option TagAliasRegistry::find( std::string const& alias ) const { + std::map::const_iterator it = m_registry.find( alias ); + if( it != m_registry.end() ) + return it->second; + else + return Option(); + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it ) { + std::size_t pos = expandedTestSpec.find( it->first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + it->second.tag + + expandedTestSpec.substr( pos + it->first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + + if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tRedefined at " << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + } + + TagAliasRegistry& TagAliasRegistry::get() { + static TagAliasRegistry instance; + return instance; + + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } + + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + try { + TagAliasRegistry::get().add( alias, tag, lineInfo ); + } + catch( std::exception& ex ) { + Colour colourGuard( Colour::Red ); + Catch::cerr() << ex.what() << std::endl; + exit(1); + } + } + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_multi.hpp +#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED + +namespace Catch { + +class MultipleReporters : public SharedImpl { + typedef std::vector > Reporters; + Reporters m_reporters; + +public: + void add( Ptr const& reporter ) { + m_reporters.push_back( reporter ); + } + +public: // IStreamingReporter + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporters[0]->getPreferences(); + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->noMatchingTestCases( spec ); + } + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunStarting( testRunInfo ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupStarting( groupInfo ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseStarting( testInfo ); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionStarting( sectionInfo ); + } + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool clearBuffer = false; + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + clearBuffer |= (*it)->assertionEnded( assertionStats ); + return clearBuffer; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionEnded( sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupEnded( testGroupStats ); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunEnded( testRunStats ); + } + + virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->skipTest( testInfo ); + } +}; + +Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { + Ptr resultingReporter; + + if( existingReporter ) { + MultipleReporters* multi = dynamic_cast( existingReporter.get() ); + if( !multi ) { + multi = new MultipleReporters; + resultingReporter = Ptr( multi ); + if( existingReporter ) + multi->add( existingReporter ); + } + else + resultingReporter = existingReporter; + multi->add( additionalReporter ); + } + else + resultingReporter = additionalReporter; + + return resultingReporter; +} + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +#include + +namespace Catch { + + struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual ~StreamingReporterBase() CATCH_OVERRIDE; + + virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector > ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() ( Ptr const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + void operator=( BySectionInfo const& ); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + ~CumulativeReporterBase(); + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} + virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + + virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + Ptr node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + Ptr node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + Ptr node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector > > m_sections; + std::vector > m_testCases; + std::vector > m_testGroups; + + std::vector > m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector > m_sectionStack; + ReporterPreferences m_reporterPrefs; + + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { + return false; + } + }; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + + template + class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ReporterRegistrar { + + class ReporterFactory : public SharedImpl { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public SharedImpl { + + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + virtual std::string getDescription() const { + return ""; + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( new ListenerFactory() ); + } + }; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void encodeTo( std::ostream& os ) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 + if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) + os << "&#x" << std::uppercase << std::hex << static_cast( c ); + else + os << c; + } + } + } + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = CATCH_NULL; + } + + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &Catch::cout() ) + {} + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &os ) + {} + + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + stream() << m_indent << "<" << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + stream() << "/>\n"; + m_tagIsOpen = false; + } + else { + stream() << m_indent << "\n"; + } + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + return *this; + } + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::ostringstream oss; + oss << attribute; + return writeAttribute( name, oss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + stream() << m_indent; + stream() << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + stream() << m_indent << ""; + m_needsNewline = true; + return *this; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + stream() << "\n"; + return *this; + } + + void setStream( std::ostream& os ) { + m_os = &os; + } + + private: + XmlWriter( XmlWriter const& ); + void operator=( XmlWriter const& ); + + std::ostream& stream() { + return *m_os; + } + + void ensureTagClosed() { + if( m_tagIsOpen ) { + stream() << ">\n"; + m_tagIsOpen = false; + } + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + stream() << "\n"; + m_needsNewline = false; + } + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream* m_os; + }; + +} +// #included from: catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_sectionDepth( 0 ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~XmlReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + + public: // StreamingReporterBase + + virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { + StreamingReporterBase::noMatchingTestCases( s ); + } + + virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testRunStarting( testInfo ); + m_xml.setStream( stream ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + } + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + const AssertionResult& assertionResult = assertionStats.assertionResult; + + // Print any info messages in tags. + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + m_xml.scopedElement( "Info" ) + .writeText( it->message ); + } else if ( it->type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( it->message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + return true; + + // Print the expression if there is one. + if( assertionResult.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", assertionResult.succeeded() ) + .writeAttribute( "type", assertionResult.getTestMacroName() ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ); + + m_xml.scopedElement( "Original" ) + .writeText( assertionResult.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( assertionResult.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( assertionResult.getResultType() ) { + case ResultWas::ThrewException: + m_xml.scopedElement( "Exception" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::FatalErrorCondition: + m_xml.scopedElement( "Fatal Error Condition" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement( "Failure" ) + .writeText( assertionResult.getMessage() ); + break; + default: + break; + } + + if( assertionResult.hasExpression() ) + m_xml.endElement(); + + return true; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + m_xml.endElement(); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include + +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~JunitReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() CATCH_OVERRIDE { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", "tbd" ); // !TBD + + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; + } + writeSection( className, "", rootSection ); + } + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + "/" + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << "\n"; + for( std::vector::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << "\n"; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +namespace Catch { + + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ) + {} + + virtual ~ConsoleReporter() CATCH_OVERRIDE; + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_headerPrinted ) { + if( m_config->showDurations() == ShowDurations::Always ) + stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + m_headerPrinted = false; + } + else { + if( m_config->showDurations() == ShowDurations::Always ) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( _testCaseStats ); + m_headerPrinted = false; + } + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << "\n" << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + printTotalsDivider( _testRunStats.totals ); + printTotals( _testRunStats.totals ); + stream << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) + { + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << "\n"; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } + else { + stream << "\n"; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << "\n"; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ":" << "\n"; + for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << "\n" << getLineOfChars<'~'>() << "\n"; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion << " host application.\n" + << "Run with -? for options\n\n"; + + if( m_config->rngSeed() != 0 ) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + if( !lineInfo.empty() ){ + stream << getLineOfChars<'-'>() << "\n"; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << "\n"; + } + stream << getLineOfChars<'.'>() << "\n" << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getLineOfChars<'.'>() << "\n"; + } + void printOpenHeader( std::string const& _name ) { + stream << getLineOfChars<'-'>() << "\n"; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; + else + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << "\n"; + } + + struct SummaryColumn { + + SummaryColumn( std::string const& _label, Colour::Code _colour ) + : label( _label ), + colour( _colour ) + {} + SummaryColumn addRow( std::size_t count ) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { + while( it->size() < row.size() ) + *it = " " + *it; + while( it->size() > row.size() ) + row = " " + row; + } + rows.push_back( row ); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + + }; + + void printTotals( Totals const& totals ) { + if( totals.testCases.total() == 0 ) { + stream << Colour( Colour::Warning ) << "No tests ran\n"; + } + else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { + stream << Colour( Colour::ResultSuccess ) << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ")" + << "\n"; + } + else { + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + columns.push_back( SummaryColumn( "passed", Colour::Success ) + .addRow( totals.testCases.passed ) + .addRow( totals.assertions.passed ) ); + columns.push_back( SummaryColumn( "failed", Colour::ResultError ) + .addRow( totals.testCases.failed ) + .addRow( totals.assertions.failed ) ); + columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + + printSummaryRow( "test cases", columns, 0 ); + printSummaryRow( "assertions", columns, 1 ); + } + } + void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { + for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { + std::string value = it->rows[row]; + if( it->label.empty() ) { + stream << label << ": "; + if( value != "0" ) + stream << value; + else + stream << Colour( Colour::Warning ) << "- none -"; + } + else if( value != "0" ) { + stream << Colour( Colour::LightGrey ) << " | "; + stream << Colour( it->colour ) + << value << " " << it->label; + } + } + stream << "\n"; + } + + static std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } + static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + void printTotalsDivider( Totals const& totals ) { + if( totals.testCases.total() > 0 ) { + std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); + std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); + std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); + while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )++; + while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )--; + + stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); + stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); + if( totals.testCases.allPassed() ) + stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); + else + stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); + } + else { + stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + } + stream << "\n"; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << "\n"; + } + + private: + bool m_headerPrinted; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType( Colour::Error, failedString() ); + printIssue( "fatal error condition with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ":"; + } + + void printResultType( Colour::Code colour, std::string passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue( std::string issue ) const { + stream << " " << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ";"; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ":"; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << "'"; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? "" : count == 2 ? "both " : "all " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : ""; + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch + +namespace Catch { + // These are all here to avoid warnings about not having any out of line + // virtual methods + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} + FileStream::~FileStream() CATCH_NOEXCEPT {} + CoutStream::~CoutStream() CATCH_NOEXCEPT {} + DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} + + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + WildcardPattern::~WildcardPattern() {} + TestSpec::Pattern::~Pattern() {} + TestSpec::NamePattern::~NamePattern() {} + TestSpec::TagPattern::~TagPattern() {} + TestSpec::ExcludedPattern::~ExcludedPattern() {} + + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + + void Config::dummy() {} + + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char* const*)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) + +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) + +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) + +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#else + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) + +#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) + +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) + +#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#else + #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +using Catch::Detail::Approx; + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/tests/config-msvc.py b/tests/config-msvc.py new file mode 100644 index 00000000..06fae62f --- /dev/null +++ b/tests/config-msvc.py @@ -0,0 +1,52 @@ +exe = "tester.exe" + +toolchain = "msvc" + +# optional +link_pool_depth = 1 + +# optional +builddir = { + "gnu" : "build" + , "msvc" : "build" + , "clang" : "build" + } + +includes = { + "gnu" : [ "-I." ] + , "msvc" : [ "/I." ] + , "clang" : [ "-I." ] + } + +defines = { + "gnu" : [ "-DEXAMPLE=1" ] + , "msvc" : [ "/DEXAMPLE=1" ] + , "clang" : [ "-DEXAMPLE=1" ] + } + +cflags = { + "gnu" : [ "-O2", "-g" ] + , "msvc" : [ "/O2" ] + , "clang" : [ "-O2", "-g" ] + } + +cxxflags = { + "gnu" : [ "-O2", "-g" ] + , "msvc" : [ "/O2" ] + , "clang" : [ "-O2", "-g", "-fsanitize=address" ] + } + +ldflags = { + "gnu" : [ ] + , "msvc" : [ ] + , "clang" : [ "-fsanitize=address" ] + } + +# optionsl +cxx_files = [ "tester.cc" ] +c_files = [ ] + +# You can register your own toolchain through register_toolchain function +def register_toolchain(ninja): + pass + diff --git a/tests/config-posix.py b/tests/config-posix.py new file mode 100644 index 00000000..29cc4d55 --- /dev/null +++ b/tests/config-posix.py @@ -0,0 +1,53 @@ +exe = "tester" + +# "gnu" or "clang" +toolchain = "gnu" + +# optional +link_pool_depth = 1 + +# optional +builddir = { + "gnu" : "build" + , "msvc" : "build" + , "clang" : "build" + } + +includes = { + "gnu" : [ "-I." ] + , "msvc" : [ "/I." ] + , "clang" : [ "-I." ] + } + +defines = { + "gnu" : [ ] + , "msvc" : [ ] + , "clang" : [ ] + } + +cflags = { + "gnu" : [ "-O2", "-g" ] + , "msvc" : [ "/O2" ] + , "clang" : [ "-O2", "-g" ] + } + +# Warn as much as possible: http://qiita.com/MitsutakaTakeda/items/6b9966f890cc9b944d75 +cxxflags = { + "gnu" : [ "-O2", "-g", "-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused", "-fsanitize=address" ] + , "msvc" : [ "/O2", "/W4" ] + , "clang" : [ "-O2", "-g", "-Werror -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic", "-fsanitize=address" ] + } + +ldflags = { + "gnu" : [ "-fsanitize=address" ] + , "msvc" : [ ] + , "clang" : [ "-fsanitize=address" ] + } + +cxx_files = [ "tester.cc" ] +c_files = [ ] + +# You can register your own toolchain through register_toolchain function +def register_toolchain(ninja): + pass + diff --git a/tests/kuroga.py b/tests/kuroga.py new file mode 100755 index 00000000..56d3f863 --- /dev/null +++ b/tests/kuroga.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python + +# +# Kuroga, single python file meta-build system for ninja +# https://github.com/lighttransport/kuroga +# +# Requirements: python 2.6 or 2.7 +# +# Usage: $ python kuroga.py input.py +# + +import imp +import re +import textwrap +import glob +import os +import sys + +# gcc preset +def add_gnu_rule(ninja): + ninja.rule('gnucxx', description='CXX $out', + command='$gnucxx -MMD -MF $out.d $gnudefines $gnuincludes $gnucxxflags -c $in -o $out', + depfile='$out.d', deps='gcc') + ninja.rule('gnucc', description='CC $out', + command='$gnucc -MMD -MF $out.d $gnudefines $gnuincludes $gnucflags -c $in -o $out', + depfile='$out.d', deps='gcc') + ninja.rule('gnulink', description='LINK $out', pool='link_pool', + command='$gnuld -o $out $in $libs $gnuldflags') + ninja.rule('gnuar', description='AR $out', pool='link_pool', + command='$gnuar rsc $out $in') + ninja.rule('gnustamp', description='STAMP $out', command='touch $out') + ninja.newline() + + ninja.variable('gnucxx', 'g++') + ninja.variable('gnucc', 'gcc') + ninja.variable('gnuld', '$gnucxx') + ninja.variable('gnuar', 'ar') + ninja.newline() + +# clang preset +def add_clang_rule(ninja): + ninja.rule('clangcxx', description='CXX $out', + command='$clangcxx -MMD -MF $out.d $clangdefines $clangincludes $clangcxxflags -c $in -o $out', + depfile='$out.d', deps='gcc') + ninja.rule('clangcc', description='CC $out', + command='$clangcc -MMD -MF $out.d $clangdefines $clangincludes $clangcflags -c $in -o $out', + depfile='$out.d', deps='gcc') + ninja.rule('clanglink', description='LINK $out', pool='link_pool', + command='$clangld -o $out $in $libs $clangldflags') + ninja.rule('clangar', description='AR $out', pool='link_pool', + command='$clangar rsc $out $in') + ninja.rule('clangstamp', description='STAMP $out', command='touch $out') + ninja.newline() + + ninja.variable('clangcxx', 'clang++') + ninja.variable('clangcc', 'clang') + ninja.variable('clangld', '$clangcxx') + ninja.variable('clangar', 'ar') + ninja.newline() + +# msvc preset +def add_msvc_rule(ninja): + ninja.rule('msvccxx', description='CXX $out', + command='$msvccxx /TP /showIncludes $msvcdefines $msvcincludes $msvccxxflags -c $in /Fo$out', + depfile='$out.d', deps='msvc') + ninja.rule('msvccc', description='CC $out', + command='$msvccc /TC /showIncludes $msvcdefines $msvcincludes $msvccflags -c $in /Fo$out', + depfile='$out.d', deps='msvc') + ninja.rule('msvclink', description='LINK $out', pool='link_pool', + command='$msvcld $msvcldflags $in $libs /OUT:$out') + ninja.rule('msvcar', description='AR $out', pool='link_pool', + command='$msvcar $in /OUT:$out') + #ninja.rule('msvcstamp', description='STAMP $out', command='touch $out') + ninja.newline() + + ninja.variable('msvccxx', 'cl.exe') + ninja.variable('msvccc', 'cl.exe') + ninja.variable('msvcld', 'link.exe') + ninja.variable('msvcar', 'lib.exe') + ninja.newline() + +# -- from ninja_syntax.py -- +def escape_path(word): + return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:') + +class Writer(object): + def __init__(self, output, width=78): + self.output = output + self.width = width + + def newline(self): + self.output.write('\n') + + def comment(self, text, has_path=False): + for line in textwrap.wrap(text, self.width - 2, break_long_words=False, + break_on_hyphens=False): + self.output.write('# ' + line + '\n') + + def variable(self, key, value, indent=0): + if value is None: + return + if isinstance(value, list): + value = ' '.join(filter(None, value)) # Filter out empty strings. + self._line('%s = %s' % (key, value), indent) + + def pool(self, name, depth): + self._line('pool %s' % name) + self.variable('depth', depth, indent=1) + + def rule(self, name, command, description=None, depfile=None, + generator=False, pool=None, restat=False, rspfile=None, + rspfile_content=None, deps=None): + self._line('rule %s' % name) + self.variable('command', command, indent=1) + if description: + self.variable('description', description, indent=1) + if depfile: + self.variable('depfile', depfile, indent=1) + if generator: + self.variable('generator', '1', indent=1) + if pool: + self.variable('pool', pool, indent=1) + if restat: + self.variable('restat', '1', indent=1) + if rspfile: + self.variable('rspfile', rspfile, indent=1) + if rspfile_content: + self.variable('rspfile_content', rspfile_content, indent=1) + if deps: + self.variable('deps', deps, indent=1) + + def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, + variables=None): + outputs = as_list(outputs) + out_outputs = [escape_path(x) for x in outputs] + all_inputs = [escape_path(x) for x in as_list(inputs)] + + if implicit: + implicit = [escape_path(x) for x in as_list(implicit)] + all_inputs.append('|') + all_inputs.extend(implicit) + if order_only: + order_only = [escape_path(x) for x in as_list(order_only)] + all_inputs.append('||') + all_inputs.extend(order_only) + + self._line('build %s: %s' % (' '.join(out_outputs), + ' '.join([rule] + all_inputs))) + + if variables: + if isinstance(variables, dict): + iterator = iter(variables.items()) + else: + iterator = iter(variables) + + for key, val in iterator: + self.variable(key, val, indent=1) + + return outputs + + def include(self, path): + self._line('include %s' % path) + + def subninja(self, path): + self._line('subninja %s' % path) + + def default(self, paths): + self._line('default %s' % ' '.join(as_list(paths))) + + def _count_dollars_before_index(self, s, i): + """Returns the number of '$' characters right in front of s[i].""" + dollar_count = 0 + dollar_index = i - 1 + while dollar_index > 0 and s[dollar_index] == '$': + dollar_count += 1 + dollar_index -= 1 + return dollar_count + + def _line(self, text, indent=0): + """Write 'text' word-wrapped at self.width characters.""" + leading_space = ' ' * indent + while len(leading_space) + len(text) > self.width: + # The text is too wide; wrap if possible. + + # Find the rightmost space that would obey our width constraint and + # that's not an escaped space. + available_space = self.width - len(leading_space) - len(' $') + space = available_space + while True: + space = text.rfind(' ', 0, space) + if (space < 0 or + self._count_dollars_before_index(text, space) % 2 == 0): + break + + if space < 0: + # No such space; just use the first unescaped space we can find. + space = available_space - 1 + while True: + space = text.find(' ', space + 1) + if (space < 0 or + self._count_dollars_before_index(text, space) % 2 == 0): + break + if space < 0: + # Give up on breaking. + break + + self.output.write(leading_space + text[0:space] + ' $\n') + text = text[space+1:] + + # Subsequent lines are continuations, so indent them. + leading_space = ' ' * (indent+2) + + self.output.write(leading_space + text + '\n') + + def close(self): + self.output.close() + + +def as_list(input): + if input is None: + return [] + if isinstance(input, list): + return input + return [input] + +# -- end from ninja_syntax.py -- + +def gen(ninja, toolchain, config): + + ninja.variable('ninja_required_version', '1.4') + ninja.newline() + + if hasattr(config, "builddir"): + builddir = config.builddir[toolchain] + ninja.variable(toolchain + 'builddir', builddir) + else: + builddir = '' + + ninja.variable(toolchain + 'defines', config.defines[toolchain] or []) + ninja.variable(toolchain + 'includes', config.includes[toolchain] or []) + ninja.variable(toolchain + 'cflags', config.cflags[toolchain] or []) + ninja.variable(toolchain + 'cxxflags', config.cxxflags[toolchain] or []) + ninja.variable(toolchain + 'ldflags', config.ldflags[toolchain] or []) + ninja.newline() + + if hasattr(config, "link_pool_depth"): + ninja.pool('link_pool', depth=config.link_pool_depth) + else: + ninja.pool('link_pool', depth=4) + ninja.newline() + + # Add default toolchain(gnu, clang and msvc) + add_gnu_rule(ninja) + add_clang_rule(ninja) + add_msvc_rule(ninja) + + obj_files = [] + + cc = toolchain + 'cc' + cxx = toolchain + 'cxx' + link = toolchain + 'link' + ar = toolchain + 'ar' + + if hasattr(config, "cxx_files"): + for src in config.cxx_files: + srcfile = src + obj = os.path.splitext(srcfile)[0] + '.o' + obj = os.path.join(builddir, obj); + obj_files.append(obj) + ninja.build(obj, cxx, srcfile) + ninja.newline() + + if hasattr(config, "c_files"): + for src in config.c_files: + srcfile = src + obj = os.path.splitext(srcfile)[0] + '.o' + obj = os.path.join(builddir, obj); + obj_files.append(obj) + ninja.build(obj, cc, srcfile) + ninja.newline() + + targetlist = [] + if hasattr(config, "exe"): + ninja.build(config.exe, link, obj_files) + targetlist.append(config.exe) + + if hasattr(config, "staticlib"): + ninja.build(config.staticlib, ar, obj_files) + targetlist.append(config.staticlib) + + ninja.build('all', 'phony', targetlist) + ninja.newline() + + ninja.default('all') + +def main(): + if len(sys.argv) < 2: + print("Usage: python kuroga.py config.py") + sys.exit(1) + + config = imp.load_source("config", sys.argv[1]) + + f = open('build.ninja', 'w') + ninja = Writer(f) + + if hasattr(config, "register_toolchain"): + config.register_toolchain(ninja) + + gen(ninja, config.toolchain, config) + f.close() + +main() diff --git a/tests/tester.cc b/tests/tester.cc new file mode 100644 index 00000000..edb92ba6 --- /dev/null +++ b/tests/tester.cc @@ -0,0 +1,324 @@ +#define TINYOBJLOADER_IMPLEMENTATION +#include "../tiny_obj_loader.h" + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +#include +#include +#include +#include +#include +#include + +static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials, bool triangulate = true) +{ + std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; + std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; + std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; + + std::cout << "# of shapes : " << shapes.size() << std::endl; + std::cout << "# of materials : " << materials.size() << std::endl; + + for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { + printf(" v[%ld] = (%f, %f, %f)\n", v, + static_cast(attrib.vertices[3*v+0]), + static_cast(attrib.vertices[3*v+1]), + static_cast(attrib.vertices[3*v+2])); + } + + for (size_t v = 0; v < attrib.normals.size() / 3; v++) { + printf(" n[%ld] = (%f, %f, %f)\n", v, + static_cast(attrib.normals[3*v+0]), + static_cast(attrib.normals[3*v+1]), + static_cast(attrib.normals[3*v+2])); + } + + for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { + printf(" uv[%ld] = (%f, %f)\n", v, + static_cast(attrib.texcoords[2*v+0]), + static_cast(attrib.texcoords[2*v+1])); + } + + for (size_t i = 0; i < shapes.size(); i++) { + printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); + printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + + if (triangulate) + { + printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); + assert((shapes[i].mesh.indices.size() % 3) == 0); + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + tinyobj::index_t i0 = shapes[i].mesh.indices[3*f+0]; + tinyobj::index_t i1 = shapes[i].mesh.indices[3*f+1]; + tinyobj::index_t i2 = shapes[i].mesh.indices[3*f+2]; + printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, + i0.vertex_index, i0.normal_index, i0.texcoord_index, + i1.vertex_index, i1.normal_index, i1.texcoord_index, + i2.vertex_index, i2.normal_index, i2.texcoord_index, + shapes[i].mesh.material_ids[f]); + } + } else { + for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { + tinyobj::index_t idx = shapes[i].mesh.indices[f]; + printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, idx.texcoord_index); + } + + printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); + assert(shapes[i].mesh.material_ids.size() == shapes[i].mesh.num_vertices.size()); + for (size_t m = 0; m < shapes[i].mesh.material_ids.size(); m++) { + printf(" material_id[%ld] = %d\n", m, + shapes[i].mesh.material_ids[m]); + } + + } + + printf("shape[%ld].num_faces: %ld\n", i, shapes[i].mesh.num_vertices.size()); + for (size_t v = 0; v < shapes[i].mesh.num_vertices.size(); v++) { + printf(" num_vertices[%ld] = %ld\n", v, + static_cast(shapes[i].mesh.num_vertices[v])); + } + + //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); + //assert((shapes[i].mesh.positions.size() % 3) == 0); + //for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { + // printf(" v[%ld] = (%f, %f, %f)\n", v, + // static_cast(shapes[i].mesh.positions[3*v+0]), + // static_cast(shapes[i].mesh.positions[3*v+1]), + // static_cast(shapes[i].mesh.positions[3*v+2])); + //} + + printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size()); + for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { + printf(" tag[%ld] = %s ", t, shapes[i].mesh.tags[t].name.c_str()); + printf(" ints: ["); + for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) + { + printf("%ld", static_cast(shapes[i].mesh.tags[t].intValues[j])); + if (j < (shapes[i].mesh.tags[t].intValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + + printf(" floats: ["); + for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) + { + printf("%f", static_cast(shapes[i].mesh.tags[t].floatValues[j])); + if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + + printf(" strings: ["); + for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) + { + printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); + if (j < (shapes[i].mesh.tags[t].stringValues.size()-1)) + { + printf(", "); + } + } + printf("]"); + printf("\n"); + } + } + + for (size_t i = 0; i < materials.size(); i++) { + printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); + printf(" material.Ka = (%f, %f ,%f)\n", static_cast(materials[i].ambient[0]), static_cast(materials[i].ambient[1]), static_cast(materials[i].ambient[2])); + printf(" material.Kd = (%f, %f ,%f)\n", static_cast(materials[i].diffuse[0]), static_cast(materials[i].diffuse[1]), static_cast(materials[i].diffuse[2])); + printf(" material.Ks = (%f, %f ,%f)\n", static_cast(materials[i].specular[0]), static_cast(materials[i].specular[1]), static_cast(materials[i].specular[2])); + printf(" material.Tr = (%f, %f ,%f)\n", static_cast(materials[i].transmittance[0]), static_cast(materials[i].transmittance[1]), static_cast(materials[i].transmittance[2])); + printf(" material.Ke = (%f, %f ,%f)\n", static_cast(materials[i].emission[0]), static_cast(materials[i].emission[1]), static_cast(materials[i].emission[2])); + printf(" material.Ns = %f\n", static_cast(materials[i].shininess)); + printf(" material.Ni = %f\n", static_cast(materials[i].ior)); + printf(" material.dissolve = %f\n", static_cast(materials[i].dissolve)); + printf(" material.illum = %d\n", materials[i].illum); + printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); + printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); + printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); + printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); + printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); + printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); + printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); + std::map::const_iterator it(materials[i].unknown_parameter.begin()); + std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); + + for (; it != itEnd; it++) { + printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); + } + printf("\n"); + } +} + +static bool +TestLoadObj( + const char* filename, + const char* basepath = NULL, + bool triangulate = true) +{ + std::cout << "Loading " << filename << std::endl; + + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + printf("Failed to load/parse .obj.\n"); + return false; + } + + PrintInfo(attrib, shapes, materials, triangulate); + + return true; +} + + +static bool +TestStreamLoadObj() +{ + std::cout << "Stream Loading " << std::endl; + + std::stringstream objStream; + objStream + << "mtllib cube.mtl\n" + "\n" + "v 0.000000 2.000000 2.000000\n" + "v 0.000000 0.000000 2.000000\n" + "v 2.000000 0.000000 2.000000\n" + "v 2.000000 2.000000 2.000000\n" + "v 0.000000 2.000000 0.000000\n" + "v 0.000000 0.000000 0.000000\n" + "v 2.000000 0.000000 0.000000\n" + "v 2.000000 2.000000 0.000000\n" + "# 8 vertices\n" + "\n" + "g front cube\n" + "usemtl white\n" + "f 1 2 3 4\n" + "g back cube\n" + "# expects white material\n" + "f 8 7 6 5\n" + "g right cube\n" + "usemtl red\n" + "f 4 3 7 8\n" + "g top cube\n" + "usemtl white\n" + "f 5 1 4 8\n" + "g left cube\n" + "usemtl green\n" + "f 5 6 2 1\n" + "g bottom cube\n" + "usemtl white\n" + "f 2 6 7 3\n" + "# 6 elements"; + +std::string matStream( + "newmtl white\n" + "Ka 0 0 0\n" + "Kd 1 1 1\n" + "Ks 0 0 0\n" + "\n" + "newmtl red\n" + "Ka 0 0 0\n" + "Kd 1 0 0\n" + "Ks 0 0 0\n" + "\n" + "newmtl green\n" + "Ka 0 0 0\n" + "Kd 0 1 0\n" + "Ks 0 0 0\n" + "\n" + "newmtl blue\n" + "Ka 0 0 0\n" + "Kd 0 0 1\n" + "Ks 0 0 0\n" + "\n" + "newmtl light\n" + "Ka 20 20 20\n" + "Kd 1 1 1\n" + "Ks 0 0 0"); + + using namespace tinyobj; + class MaterialStringStreamReader: + public MaterialReader + { + public: + MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {} + virtual ~MaterialStringStreamReader() {} + virtual bool operator() ( + const std::string& matId, + std::vector* materials, + std::map* matMap, + std::string* err) + { + (void)matId; + (void)err; + LoadMtl(matMap, materials, &m_matSStream); + return true; + } + + private: + std::stringstream m_matSStream; + }; + + MaterialStringStreamReader matSSReader(matStream); + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + return false; + } + + PrintInfo(attrib, shapes, materials); + + return true; +} + +const char* gMtlBasePath = "../models"; + +TEST_CASE("cornell_box", "[Loader]") { + + REQUIRE(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath)); +} + +#if 0 +int +main( + int argc, + char **argv) +{ + if (argc > 1) { + const char* basepath = NULL; + if (argc > 2) { + basepath = argv[2]; + } + assert(true == TestLoadObj(argv[1], basepath)); + } else { + //assert(true == TestLoadObj("cornell_box.obj")); + //assert(true == TestLoadObj("cube.obj")); + assert(true == TestStreamLoadObj()); + assert(true == TestLoadObj("catmark_torus_creases0.obj", NULL, false)); + } + + return 0; +} +#endif diff --git a/tests/vcbuild.bat b/tests/vcbuild.bat new file mode 100644 index 00000000..3dbc6c16 --- /dev/null +++ b/tests/vcbuild.bat @@ -0,0 +1,3 @@ +chcp 437 +call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 +ninja diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index e67bed32..9a03489e 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -111,6 +111,33 @@ typedef struct { std::vector texcoords; // 'vt' } attrib_t; +typedef struct callback_t_ { + void (*vertex_cb)(void *user_data, float x, float y, float z); + void (*normal_cb)(void *user_data, float x, float y, float z); + void (*texcoord_cb)(void *user_data, float x, float y); + // -2147483648 will be passed for undefined index + void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx); + // Index to material + void (*usemtl_cb)(void *user_data, int material_idx); + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t_() : + vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) { + } + +} callback_t; + class MaterialReader { public: MaterialReader() {} @@ -138,7 +165,6 @@ class MaterialFileReader : public MaterialReader { /// Loads .obj from a file. /// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data /// 'shapes' will be filled with parsed shape data -/// The function returns error string. /// Returns true when loading .obj become success. /// Returns warning and error message into `err` /// 'mtl_basepath' is optional, and used for base path for .mtl file. @@ -149,6 +175,18 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, const char *filename, const char *mtl_basepath = NULL, bool triangulate = true); +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning and error message into `err` +/// 'mtl_basepath' is optional, and used for base path for .mtl file. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +bool LoadObjWithCallback(void *user_data, const callback_t &callback, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn); + /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. /// Returns true when loading .obj become success. @@ -418,7 +456,7 @@ static tag_sizes parseTagTriple(const char **token) { return ts; } -// Parse triples: i, i/j/k, i//k, i/j +// Parse triples with index offsets: i, i/j/k, i//k, i/j static vertex_index parseTriple(const char **token, int vsize, int vnsize, int vtsize) { vertex_index vi(-1); @@ -452,6 +490,39 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize, return vi; } +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index parseRawTriple(const char **token) { + vertex_index vi(0x8000000); // -2147483648 = invalid + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + static void InitMaterial(material_t *material) { material->name = ""; material->ambient_texname = ""; @@ -831,7 +902,6 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // material std::map material_map; - // std::map vertexCache; int material = -1; shape_t shape; @@ -1103,6 +1173,270 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, return true; } +bool LoadObjWithCallback(void *user_data, const callback_t &callback, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn) { + std::stringstream errss; + + std::string name; + + // material + std::map material_map; + int material = -1; + + shape_t shape; + + int maxchars = 8192; // Alloc enough size. + std::vector buf(static_cast(maxchars)); // Alloc enough size. + while (inStream->peek() != -1) { + inStream->getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + float x, y, z; + parseFloat3(&x, &y, &z, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + float x, y; + parseFloat2(&x, &y, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + while (!IS_NEW_LINE(token[0])) { + vertex_index vi = parseRawTriple(&token); + if (callback.index_cb) { + callback.index_cb(user_data, vi.v_idx, vi.vn_idx, vi.vt_idx); + } + size_t n = strspn(token, " \t\r"); + token += n; + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material) { + material = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, material); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + + std::string err_mtl; + std::vector materials; + bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl); + if (err) { + (*err) += err_mtl; + } + + if (!ok) { + return false; + } + + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + std::vector names; + names.reserve(2); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + std::vector tmp(names.size() - 1); + for (size_t j = 0; j < tmp.size(); j++) { + tmp[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &tmp.at(0), + static_cast(tmp.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + name = std::string(namebuf); + + if (callback.object_cb) { + callback.object_cb(user_data, name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_floats)); + for (size_t i = 0; i < static_cast(ts.num_floats); ++i) { + tag.floatValues[i] = parseFloat(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; + +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); +#else + sscanf(token, "%s", stringValueBuffer); +#endif + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); +#endif + } + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} } // namespace tinyobj #endif From 93acf631571ded19556a4f4684541f5ecb3c8f10 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 16:48:37 +0900 Subject: [PATCH 108/585] Change API for usemtl callback. --- tiny_obj_loader.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 9a03489e..d46c704d 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -117,8 +117,9 @@ typedef struct callback_t_ { void (*texcoord_cb)(void *user_data, float x, float y); // -2147483648 will be passed for undefined index void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx); - // Index to material - void (*usemtl_cb)(void *user_data, int material_idx); + // `name` material name, `materialId` = the array index of material_t[]. -1 if a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char* name, int materialId); + // `materials` = parsed material data. void (*mtllib_cb)(void *user_data, const material_t *materials, int num_materials); // There may be multiple group names @@ -1178,13 +1179,9 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback, MaterialReader *readMatFn) { std::stringstream errss; - std::string name; - // material std::map material_map; - int material = -1; - - shape_t shape; + int materialId = -1; // -1 = invalid int maxchars = 8192; // Alloc enough size. std::vector buf(static_cast(maxchars)); // Alloc enough size. @@ -1284,12 +1281,12 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback, // { error!! material not found } } - if (newMaterialId != material) { - material = newMaterialId; + if (newMaterialId != materialId) { + materialId = newMaterialId; } if (callback.usemtl_cb) { - callback.usemtl_cb(user_data, material); + callback.usemtl_cb(user_data, namebuf, materialId); } continue; @@ -1337,6 +1334,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback, assert(names.size() > 0); + std::string name; // names[0] must be 'g', so skip the 0th element. if (names.size() > 1) { name = names[1]; From 1e663342bf8d3ea41378e536abd26a609cb45ca0 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 17:52:04 +0900 Subject: [PATCH 109/585] Add callback API example. Fix a sentinel value for the vertex index. --- README.md | 5 ++ examples/callback_api/Makefile | 2 + examples/callback_api/main.cc | 155 +++++++++++++++++++++++++++++++++ tiny_obj_loader.h | 2 +- 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 examples/callback_api/Makefile create mode 100644 examples/callback_api/main.cc diff --git a/README.md b/README.md index 056f7c3a..a180d701 100644 --- a/README.md +++ b/README.md @@ -193,3 +193,8 @@ for (size_t i = 0; i < shapes.size(); i++) { } ``` + +Tests +----- + +Unit tests are provided. see `tests` directory for details. diff --git a/examples/callback_api/Makefile b/examples/callback_api/Makefile new file mode 100644 index 00000000..45d60d85 --- /dev/null +++ b/examples/callback_api/Makefile @@ -0,0 +1,2 @@ +all: + clang++ -I../../ -Wall -g -o example main.cc diff --git a/examples/callback_api/main.cc b/examples/callback_api/main.cc new file mode 100644 index 00000000..4f492d02 --- /dev/null +++ b/examples/callback_api/main.cc @@ -0,0 +1,155 @@ +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + +#include +#include +#include +#include +#include +#include + +typedef struct +{ + std::vector vertices; + std::vector normals; + std::vector texcoords; + std::vector v_indices; + std::vector vn_indices; + std::vector vt_indices; + + std::vector materials; + +} MyMesh; + +void vertex_cb(void *user_data, float x, float y, float z) +{ + MyMesh *mesh = reinterpret_cast(user_data); + printf("v[%ld] = %f, %f, %f\n", mesh->vertices.size() / 3, x, y, z); + + mesh->vertices.push_back(x); + mesh->vertices.push_back(y); + mesh->vertices.push_back(z); +} + +void normal_cb(void *user_data, float x, float y, float z) +{ + MyMesh *mesh = reinterpret_cast(user_data); + printf("vn[%ld] = %f, %f, %f\n", mesh->normals.size() / 3, x, y, z); + + mesh->normals.push_back(x); + mesh->normals.push_back(y); + mesh->normals.push_back(z); +} + +void texcoord_cb(void *user_data, float x, float y) +{ + MyMesh *mesh = reinterpret_cast(user_data); + printf("vt[%ld] = %f, %f\n", mesh->texcoords.size() / 2, x, y); + + mesh->texcoords.push_back(x); + mesh->texcoords.push_back(y); +} + +void index_cb(void *user_data, int v_idx, int vn_idx, int vt_idx) +{ + // NOTE: the value of each index is raw value. + // For example, the application must manually adjust the index with offset + // (e.g. v_indices.size()) when the value is negative(relative index). + // See fixIndex() function in tiny_obj_loader.h for details. + // Also, -2147483648(0x80000000) is set for the index value which does not exist in .obj + MyMesh *mesh = reinterpret_cast(user_data); + printf("idx[%ld] = %d, %d, %d\n", mesh->v_indices.size(), v_idx, vn_idx, vt_idx); + + if (v_idx != 0x80000000) { + mesh->v_indices.push_back(v_idx); + } + if (vn_idx != 0x80000000) { + mesh->vn_indices.push_back(vn_idx); + } + if (vt_idx != 0x80000000) { + mesh->vt_indices.push_back(vt_idx); + } +} + +void usemtl_cb(void *user_data, const char* name, int material_idx) +{ + MyMesh *mesh = reinterpret_cast(user_data); + if ((material_idx > -1) && (material_idx < mesh->materials.size())) { + printf("usemtl. material id = %d(name = %s)\n", material_idx, mesh->materials[material_idx].name.c_str()); + } else { + printf("usemtl. name = %s\n", name); + } +} + +void mtllib_cb(void *user_data, const tinyobj::material_t *materials, int num_materials) +{ + MyMesh *mesh = reinterpret_cast(user_data); + printf("mtllib. # of materials = %d\n", num_materials); + + for (int i = 0; i < num_materials; i++) { + mesh->materials.push_back(materials[i]); + } +} + +void group_cb(void *user_data, const char **names, int num_names) +{ + //MyMesh *mesh = reinterpret_cast(user_data); + printf("group : name = \n"); + + for (int i = 0; i < num_names; i++) { + printf(" %s\n", names[i]); + } +} + +void object_cb(void *user_data, const char *name) +{ + //MyMesh *mesh = reinterpret_cast(user_data); + printf("object : name = %s\n", name); + +} + +int +main(int argc, char** argv) +{ + tinyobj::callback_t cb; + cb.vertex_cb = vertex_cb; + cb.normal_cb = normal_cb; + cb.texcoord_cb = texcoord_cb; + cb.index_cb = index_cb; + cb.usemtl_cb = usemtl_cb; + cb.mtllib_cb = mtllib_cb; + cb.group_cb = group_cb; + cb.object_cb = object_cb; + + MyMesh mesh; + std::string err; + std::ifstream ifs("../../models/cornell_box.obj"); + + if (ifs.fail()) { + std::cerr << "file not found." << std::endl; + return EXIT_FAILURE; + } + + tinyobj::MaterialFileReader mtlReader("../../models/"); + + bool ret = tinyobj::LoadObjWithCallback(&mesh, cb, &err, &ifs, &mtlReader); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + std::cerr << "Failed to parse .obj" << std::endl; + return EXIT_FAILURE; + } + + printf("# of vertices = %ld\n", mesh.vertices.size() / 3); + printf("# of normals = %ld\n", mesh.normals.size() / 3); + printf("# of texcoords = %ld\n", mesh.texcoords.size() / 2); + printf("# of vertex indices = %ld\n", mesh.v_indices.size()); + printf("# of normal indices = %ld\n", mesh.vn_indices.size()); + printf("# of texcoord indices = %ld\n", mesh.vt_indices.size()); + printf("# of materials = %ld\n", mesh.materials.size()); + + return EXIT_SUCCESS; +} diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index d46c704d..d2aae380 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -493,7 +493,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize, // Parse raw triples: i, i/j/k, i//k, i/j static vertex_index parseRawTriple(const char **token) { - vertex_index vi(0x8000000); // -2147483648 = invalid + vertex_index vi(0x80000000); // 0x80000000 = -2147483648 = invalid vi.v_idx = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); From 90d33c33c02359b3a545664cd60eae84d04788a2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 17:59:17 +0900 Subject: [PATCH 110/585] Fix CI scripts. --- .travis.yml | 11 ++--------- tests/Makefile | 5 ++++- wercker.yml | 6 ++---- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index bde52f6f..3ec9e45a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,15 +42,8 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi script: -- mkdir build && cd build -- export CC="${CC}-${COMPILER_VERSION}" -- export CXX="${CXX}-${COMPILER_VERSION}" -- ${CC} -v -- cmake --version -- cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTINYOBJLOADER_BUILD_TEST_LOADER=On -G Ninja - .. -- ninja -- ./test_loader ../cornell_box.obj +- cd tests +- make check - if [ -n "$REPORT_COVERAGE" ]; then coveralls -b . -r .. -e examples -e tools -e jni -e python -e images -E ".*CompilerId.*" -E ".*feature_tests.*" ; fi - cd .. diff --git a/tests/Makefile b/tests/Makefile index 1a1434ab..4a18c713 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,10 @@ .PHONY: clean +CXX ?= g++ +CXXFLAGS ?= -g -O2 + tester: tester.cc - g++ -g -O0 -o tester tester.cc + $(CXX) $(CXXFLAGS) -o tester tester.cc all: tester diff --git a/wercker.yml b/wercker.yml index 3c1583c1..1d1aa26f 100644 --- a/wercker.yml +++ b/wercker.yml @@ -6,7 +6,5 @@ build: name: build code: | git clone https://github.com/syoyo/orebuildenv.git - chmod +x ./orebuildenv/build/linux/bin/premake4 - ./orebuildenv/build/linux/bin/premake4 gmake - make - ./test_tinyobjloader + cd tests + make check From 153de2b3f04a44245ae6acd09eeaf0fff2e3efe0 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 19:29:59 +0900 Subject: [PATCH 111/585] Update Android build. --- README.md | 90 +++----------------------------------------------- jni/Android.mk | 4 +-- 2 files changed, 6 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index a180d701..35dfd1e2 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,13 @@ Features * Material * Unknown material attributes are returned as key-value(value is string) map. * Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) +* Callback API for custom loading. TODO ---- -* [ ] Support different indices for vertex/normal/texcoord +* [ ] Read .obj/.mtl from memory License ------- @@ -88,7 +89,6 @@ Licensed under 2 clause BSD. Usage ----- -TinyObjLoader triangulate input .obj by default. ```c++ #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc #include "tiny_obj_loader.h" @@ -108,93 +108,11 @@ if (!ret) { exit(1); } -std::cout << "# of shapes : " << shapes.size() << std::endl; -std::cout << "# of materials : " << materials.size() << std::endl; - -for (size_t i = 0; i < shapes.size(); i++) { - printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); - } - - printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - assert((shapes[i].mesh.positions.size() % 3) == 0); - for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { - printf(" v[%ld] = (%f, %f, %f)\n", v, - shapes[i].mesh.positions[3*v+0], - shapes[i].mesh.positions[3*v+1], - shapes[i].mesh.positions[3*v+2]); - } -} - -for (size_t i = 0; i < materials.size(); i++) { - printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]); - printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]); - printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]); - printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]); - printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]); - printf(" material.Ns = %f\n", materials[i].shininess); - printf(" material.Ni = %f\n", materials[i].ior); - printf(" material.dissolve = %f\n", materials[i].dissolve); - printf(" material.illum = %d\n", materials[i].illum); - printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); - printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); - printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); - std::map::const_iterator it(materials[i].unknown_parameter.begin()); - std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); - for (; it != itEnd; it++) { - printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); - } - printf("\n"); -} +// See loader_example.cc for more details. ``` -Reading .obj without triangulation. Use `num_vertices[i]` to iterate over faces(indices). `num_vertices[i]` stores the number of vertices for ith face. -```c++ -#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc -#include "tiny_obj_loader.h" - -std::string inputfile = "cornell_box.obj"; -std::vector shapes; -std::vector materials; - -std::string err; -bool triangulate = false; -bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str(), triangulate); - -if (!err.empty()) { // `err` may contain warning message. - std::cerr << err << std::endl; -} - -if (!ret) { - exit(1); -} - -for (size_t i = 0; i < shapes.size(); i++) { - - size_t indexOffset = 0; - for (size_t n = 0; n < shapes[i].mesh.num_vertices.size(); n++) { - int ngon = shapes[i].mesh.num_vertices[n]; - for (size_t f = 0; f < ngon; f++) { - unsigned int v = shapes[i].mesh.indices[indexOffset + f]; - printf(" face[%ld] v[%ld] = (%f, %f, %f)\n", n, - shapes[i].mesh.positions[3*v+0], - shapes[i].mesh.positions[3*v+1], - shapes[i].mesh.positions[3*v+2]); - - } - indexOffset += ngon; - } - -} -``` Tests ----- -Unit tests are provided. see `tests` directory for details. +Unit tests are provided in `tests` directory. See `tests/README.md` for details. diff --git a/jni/Android.mk b/jni/Android.mk index eaed1b78..bc81f8f7 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -5,8 +5,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := tinyobjloader -LOCAL_SRC_FILES := ../tiny_obj_loader.cc ../test.cc +LOCAL_SRC_FILES := ../tiny_obj_loader.cc LOCAL_C_INCLUDES := ../ -include $(BUILD_EXECUTABLE) +include $(BUILD_STATIC_LIBRARY) From 1703ab087db0e90fc807ddd176a1ea426b7cf7a5 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 18 Apr 2016 20:07:04 +0900 Subject: [PATCH 112/585] Add simple OpenGL viewer example. --- examples/viewer/Makefile | 8 + examples/viewer/README.md | 1 + examples/viewer/trackball.cc | 292 +++++++++++++++++++++++++ examples/viewer/trackball.h | 75 +++++++ examples/viewer/viewer.cc | 403 +++++++++++++++++++++++++++++++++++ 5 files changed, 779 insertions(+) create mode 100644 examples/viewer/Makefile create mode 100644 examples/viewer/README.md create mode 100644 examples/viewer/trackball.cc create mode 100644 examples/viewer/trackball.h create mode 100644 examples/viewer/viewer.cc diff --git a/examples/viewer/Makefile b/examples/viewer/Makefile new file mode 100644 index 00000000..e5204fca --- /dev/null +++ b/examples/viewer/Makefile @@ -0,0 +1,8 @@ +GLFW_INC=-I/usr/local/include +GLFW_LIBS=-L/usr/local/lib -lglfw3 -lglew +GL_LIBS=-framework OpenGL + +CXX_FLAGS=-Wno-deprecated-declarations + +all: + g++ -fsanitize=address -O0 -g -o objviewer $(CXX_FLAGS) viewer.cc ../../tiny_obj_loader.cc trackball.cc $(GLFW_INC) $(GL_LIBS) $(GLFW_LIBS) diff --git a/examples/viewer/README.md b/examples/viewer/README.md new file mode 100644 index 00000000..4e0e0872 --- /dev/null +++ b/examples/viewer/README.md @@ -0,0 +1 @@ +Simple .obj viewer with glew + glfw3 + OpenGL diff --git a/examples/viewer/trackball.cc b/examples/viewer/trackball.cc new file mode 100644 index 00000000..86ff3b3f --- /dev/null +++ b/examples/viewer/trackball.cc @@ -0,0 +1,292 @@ +/* + * (c) Copyright 1993, 1994, Silicon Graphics, Inc. + * ALL RIGHTS RESERVED + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the above + * copyright notice appear in all copies and that both the copyright notice + * and this permission notice appear in supporting documentation, and that + * the name of Silicon Graphics, Inc. not be used in advertising + * or publicity pertaining to distribution of the software without specific, + * written prior permission. + * + * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" + * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, + * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY + * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, + * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF + * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE + * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. + * + * US Government Users Restricted Rights + * Use, duplication, or disclosure by the Government is subject to + * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph + * (c)(1)(ii) of the Rights in Technical Data and Computer Software + * clause at DFARS 252.227-7013 and/or in similar or successor + * clauses in the FAR or the DOD or NASA FAR Supplement. + * Unpublished-- rights reserved under the copyright laws of the + * United States. Contractor/manufacturer is Silicon Graphics, + * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. + * + * OpenGL(TM) is a trademark of Silicon Graphics, Inc. + */ +/* + * Trackball code: + * + * Implementation of a virtual trackball. + * Implemented by Gavin Bell, lots of ideas from Thant Tessman and + * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. + * + * Vector manip code: + * + * Original code from: + * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli + * + * Much mucking with by: + * Gavin Bell + */ +#include +#include "trackball.h" + +/* + * This size should really be based on the distance from the center of + * rotation to the point on the object underneath the mouse. That + * point would then track the mouse as closely as possible. This is a + * simple example, though, so that is left as an Exercise for the + * Programmer. + */ +#define TRACKBALLSIZE (0.8) + +/* + * Local function prototypes (not defined in trackball.h) + */ +static float tb_project_to_sphere(float, float, float); +static void normalize_quat(float[4]); + +static void vzero(float *v) { + v[0] = 0.0; + v[1] = 0.0; + v[2] = 0.0; +} + +static void vset(float *v, float x, float y, float z) { + v[0] = x; + v[1] = y; + v[2] = z; +} + +static void vsub(const float *src1, const float *src2, float *dst) { + dst[0] = src1[0] - src2[0]; + dst[1] = src1[1] - src2[1]; + dst[2] = src1[2] - src2[2]; +} + +static void vcopy(const float *v1, float *v2) { + register int i; + for (i = 0; i < 3; i++) + v2[i] = v1[i]; +} + +static void vcross(const float *v1, const float *v2, float *cross) { + float temp[3]; + + temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); + temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); + temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); + vcopy(temp, cross); +} + +static float vlength(const float *v) { + return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} + +static void vscale(float *v, float div) { + v[0] *= div; + v[1] *= div; + v[2] *= div; +} + +static void vnormal(float *v) { vscale(v, 1.0 / vlength(v)); } + +static float vdot(const float *v1, const float *v2) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +static void vadd(const float *src1, const float *src2, float *dst) { + dst[0] = src1[0] + src2[0]; + dst[1] = src1[1] + src2[1]; + dst[2] = src1[2] + src2[2]; +} + +/* + * Ok, simulate a track-ball. Project the points onto the virtual + * trackball, then figure out the axis of rotation, which is the cross + * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) + * Note: This is a deformed trackball-- is a trackball in the center, + * but is deformed into a hyperbolic sheet of rotation away from the + * center. This particular function was chosen after trying out + * several variations. + * + * It is assumed that the arguments to this routine are in the range + * (-1.0 ... 1.0) + */ +void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) { + float a[3]; /* Axis of rotation */ + float phi; /* how much to rotate about axis */ + float p1[3], p2[3], d[3]; + float t; + + if (p1x == p2x && p1y == p2y) { + /* Zero rotation */ + vzero(q); + q[3] = 1.0; + return; + } + + /* + * First, figure out z-coordinates for projection of P1 and P2 to + * deformed sphere + */ + vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y)); + vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y)); + + /* + * Now, we want the cross product of P1 and P2 + */ + vcross(p2, p1, a); + + /* + * Figure out how much to rotate around that axis. + */ + vsub(p1, p2, d); + t = vlength(d) / (2.0 * TRACKBALLSIZE); + + /* + * Avoid problems with out-of-control values... + */ + if (t > 1.0) + t = 1.0; + if (t < -1.0) + t = -1.0; + phi = 2.0 * asin(t); + + axis_to_quat(a, phi, q); +} + +/* + * Given an axis and angle, compute quaternion. + */ +void axis_to_quat(float a[3], float phi, float q[4]) { + vnormal(a); + vcopy(a, q); + vscale(q, sin(phi / 2.0)); + q[3] = cos(phi / 2.0); +} + +/* + * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet + * if we are away from the center of the sphere. + */ +static float tb_project_to_sphere(float r, float x, float y) { + float d, t, z; + + d = sqrt(x * x + y * y); + if (d < r * 0.70710678118654752440) { /* Inside sphere */ + z = sqrt(r * r - d * d); + } else { /* On hyperbola */ + t = r / 1.41421356237309504880; + z = t * t / d; + } + return z; +} + +/* + * Given two rotations, e1 and e2, expressed as quaternion rotations, + * figure out the equivalent single rotation and stuff it into dest. + * + * This routine also normalizes the result every RENORMCOUNT times it is + * called, to keep error from creeping in. + * + * NOTE: This routine is written so that q1 or q2 may be the same + * as dest (or each other). + */ + +#define RENORMCOUNT 97 + +void add_quats(float q1[4], float q2[4], float dest[4]) { + static int count = 0; + float t1[4], t2[4], t3[4]; + float tf[4]; + + vcopy(q1, t1); + vscale(t1, q2[3]); + + vcopy(q2, t2); + vscale(t2, q1[3]); + + vcross(q2, q1, t3); + vadd(t1, t2, tf); + vadd(t3, tf, tf); + tf[3] = q1[3] * q2[3] - vdot(q1, q2); + + dest[0] = tf[0]; + dest[1] = tf[1]; + dest[2] = tf[2]; + dest[3] = tf[3]; + + if (++count > RENORMCOUNT) { + count = 0; + normalize_quat(dest); + } +} + +/* + * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 + * If they don't add up to 1.0, dividing by their magnitued will + * renormalize them. + * + * Note: See the following for more information on quaternions: + * + * - Shoemake, K., Animating rotation with quaternion curves, Computer + * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. + * - Pletinckx, D., Quaternion calculus as a basic tool in computer + * graphics, The Visual Computer 5, 2-13, 1989. + */ +static void normalize_quat(float q[4]) { + int i; + float mag; + + mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); + for (i = 0; i < 4; i++) + q[i] /= mag; +} + +/* + * Build a rotation matrix, given a quaternion rotation. + * + */ +void build_rotmatrix(float m[4][4], const float q[4]) { + m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); + m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); + m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); + m[0][3] = 0.0; + + m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); + m[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); + m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); + m[1][3] = 0.0; + + m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); + m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); + m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); + m[2][3] = 0.0; + + m[3][0] = 0.0; + m[3][1] = 0.0; + m[3][2] = 0.0; + m[3][3] = 1.0; +} diff --git a/examples/viewer/trackball.h b/examples/viewer/trackball.h new file mode 100644 index 00000000..b1f94374 --- /dev/null +++ b/examples/viewer/trackball.h @@ -0,0 +1,75 @@ +/* + * (c) Copyright 1993, 1994, Silicon Graphics, Inc. + * ALL RIGHTS RESERVED + * Permission to use, copy, modify, and distribute this software for + * any purpose and without fee is hereby granted, provided that the above + * copyright notice appear in all copies and that both the copyright notice + * and this permission notice appear in supporting documentation, and that + * the name of Silicon Graphics, Inc. not be used in advertising + * or publicity pertaining to distribution of the software without specific, + * written prior permission. + * + * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" + * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, + * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY + * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, + * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF + * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE + * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. + * + * US Government Users Restricted Rights + * Use, duplication, or disclosure by the Government is subject to + * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph + * (c)(1)(ii) of the Rights in Technical Data and Computer Software + * clause at DFARS 252.227-7013 and/or in similar or successor + * clauses in the FAR or the DOD or NASA FAR Supplement. + * Unpublished-- rights reserved under the copyright laws of the + * United States. Contractor/manufacturer is Silicon Graphics, + * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. + * + * OpenGL(TM) is a trademark of Silicon Graphics, Inc. + */ +/* + * trackball.h + * A virtual trackball implementation + * Written by Gavin Bell for Silicon Graphics, November 1988. + */ + +/* + * Pass the x and y coordinates of the last and current positions of + * the mouse, scaled so they are from (-1.0 ... 1.0). + * + * The resulting rotation is returned as a quaternion rotation in the + * first paramater. + */ +void trackball(float q[4], float p1x, float p1y, float p2x, float p2y); + +void negate_quat(float *q, float *qn); + +/* + * Given two quaternions, add them together to get a third quaternion. + * Adding quaternions to get a compound rotation is analagous to adding + * translations to get a compound translation. When incrementally + * adding rotations, the first argument here should be the new + * rotation, the second and third the total rotation (which will be + * over-written with the resulting new total rotation). + */ +void add_quats(float *q1, float *q2, float *dest); + +/* + * A useful function, builds a rotation matrix in Matrix based on + * given quaternion. + */ +void build_rotmatrix(float m[4][4], const float q[4]); + +/* + * This function computes a quaternion based on an axis (defined by + * the given vector) and an angle about which to rotate. The angle is + * expressed in radians. The result is put into the third argument. + */ +void axis_to_quat(float a[3], float phi, float q[4]); diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc new file mode 100644 index 00000000..d89fadc4 --- /dev/null +++ b/examples/viewer/viewer.cc @@ -0,0 +1,403 @@ +// +// Simple .obj viewer(vertex only) +// +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include + +#include "../../tiny_obj_loader.h" + +#include "trackball.h" + +typedef struct { + GLuint vb; // vertex buffer + int numTriangles; +} DrawObject; + +std::vector gDrawObjects; + +int width = 512; +int height = 512; + +double prevMouseX, prevMouseY; +bool mouseLeftPressed; +bool mouseMiddlePressed; +bool mouseRightPressed; +float curr_quat[4]; +float prev_quat[4]; +float eye[3], lookat[3], up[3]; + +GLFWwindow* window; + +void CheckErrors(std::string desc) { + GLenum e = glGetError(); + if (e != GL_NO_ERROR) { + fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); + exit(20); + } +} + +void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { + float v10[3]; + v10[0] = v1[0] - v0[0]; + v10[1] = v1[1] - v0[1]; + v10[2] = v1[2] - v0[2]; + + float v20[3]; + v20[0] = v2[0] - v0[0]; + v20[1] = v2[1] - v0[1]; + v20[2] = v2[2] - v0[2]; + + N[0] = v20[1] * v10[2] - v20[2] * v10[1]; + N[1] = v20[2] * v10[0] - v20[0] * v10[2]; + N[2] = v20[0] * v10[1] - v20[1] * v10[0]; + + float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; + if (len2 > 0.0f) { + float len = sqrtf(len2); + + N[0] /= len; + N[1] /= len; + } +} + +bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector& drawObjects, const char* filename) +{ + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, NULL); + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + std::cerr << "Failed to load " << filename << std::endl; + return false; + } + + printf("# of materials = %d\n", (int)materials.size()); + printf("# of shapes = %d\n", (int)shapes.size()); + + bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); + bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); + + { + for (size_t s = 0; s < shapes.size(); s++) { + DrawObject o; + std::vector vb; // pos(3float), normal(3float) + for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) { + + tinyobj::index_t idx0 = shapes[s].mesh.indices[3*f+0]; + tinyobj::index_t idx1 = shapes[s].mesh.indices[3*f+1]; + tinyobj::index_t idx2 = shapes[s].mesh.indices[3*f+2]; + + float v[3][3]; + for (int k = 0; k < 3; k++) { + int f0 = idx0.vertex_index; + int f1 = idx1.vertex_index; + int f2 = idx2.vertex_index; + + v[0][k] = attrib.vertices[3*f0+k]; + v[1][k] = attrib.vertices[3*f1+k]; + v[2][k] = attrib.vertices[3*f2+k]; + bmin[k] = std::min(v[0][k], bmin[k]); + bmin[k] = std::min(v[1][k], bmin[k]); + bmin[k] = std::min(v[2][k], bmin[k]); + bmax[k] = std::max(v[0][k], bmax[k]); + bmax[k] = std::max(v[1][k], bmax[k]); + bmax[k] = std::max(v[2][k], bmax[k]); + } + + float n[3][3]; + + if (attrib.normals.size() > 0) { + int f0 = idx0.normal_index; + int f1 = idx1.normal_index; + int f2 = idx2.normal_index; + for (int k = 0; k < 3; k++) { + n[0][k] = attrib.normals[3*f0+k]; + n[1][k] = attrib.normals[3*f1+k]; + n[2][k] = attrib.normals[3*f2+k]; + } + } else { + // compute geometric normal + CalcNormal(n[0], v[0], v[1], v[2]); + n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2]; + n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2]; + } + + for (int k = 0; k < 3; k++) { + vb.push_back(v[k][0]); + vb.push_back(v[k][1]); + vb.push_back(v[k][2]); + vb.push_back(n[k][0]); + vb.push_back(n[k][1]); + vb.push_back(n[k][2]); + } + + } + + o.vb = 0; + o.numTriangles = 0; + if (vb.size() > 0) { + glGenBuffers(1, &o.vb); + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); + o.numTriangles = vb.size() / 6 / 3; + } + + gDrawObjects.push_back(o); + } + } + + printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); + printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); + + return true; +} + +void reshapeFunc(GLFWwindow* window, int w, int h) +{ + printf("reshape\n"); + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0, (float)w / (float)h, 0.1f, 1000.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + width = w; + height = h; +} + +void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) { + if(action == GLFW_PRESS || action == GLFW_REPEAT){ + // Move camera + float mv_x = 0, mv_y = 0, mv_z = 0; + if(key == GLFW_KEY_K) mv_x += 1; + else if(key == GLFW_KEY_J) mv_x += -1; + else if(key == GLFW_KEY_L) mv_y += 1; + else if(key == GLFW_KEY_H) mv_y += -1; + else if(key == GLFW_KEY_P) mv_z += 1; + else if(key == GLFW_KEY_N) mv_z += -1; + //camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05); + // Close window + if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE); + + //init_frame = true; + } +} + +void clickFunc(GLFWwindow* window, int button, int action, int mods){ + if(button == GLFW_MOUSE_BUTTON_LEFT){ + if(action == GLFW_PRESS){ + mouseLeftPressed = true; + trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); + } else if(action == GLFW_RELEASE){ + mouseLeftPressed = false; + } + } + if(button == GLFW_MOUSE_BUTTON_RIGHT){ + if(action == GLFW_PRESS){ + mouseRightPressed = true; + } else if(action == GLFW_RELEASE){ + mouseRightPressed = false; + } + } + if(button == GLFW_MOUSE_BUTTON_MIDDLE){ + if(action == GLFW_PRESS){ + mouseMiddlePressed = true; + } else if(action == GLFW_RELEASE){ + mouseMiddlePressed = false; + } + } +} + +void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){ + float rotScale = 1.0f; + float transScale = 2.0f; + + if(mouseLeftPressed){ + trackball(prev_quat, + rotScale * (2.0f * prevMouseX - width) / (float)width, + rotScale * (height - 2.0f * prevMouseY) / (float)height, + rotScale * (2.0f * mouse_x - width) / (float)width, + rotScale * (height - 2.0f * mouse_y) / (float)height); + + add_quats(prev_quat, curr_quat, curr_quat); + } else if (mouseMiddlePressed) { + eye[0] += transScale * (mouse_x - prevMouseX) / (float)width; + lookat[0] += transScale * (mouse_x - prevMouseX) / (float)width; + eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; + lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; + } else if (mouseRightPressed) { + eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; + lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; + } + + // Update mouse point + prevMouseX = mouse_x; + prevMouseY = mouse_y; +} + +void Draw(const std::vector& drawObjects) +{ + glPolygonMode(GL_FRONT, GL_FILL); + glPolygonMode(GL_BACK, GL_FILL); + + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1.0); + glColor3f(1.0f, 1.0f, 1.0f); + for (size_t i = 0; i < drawObjects.size(); i++) { + DrawObject o = drawObjects[i]; + if (o.vb < 1) { + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, 24, (const void*)0); + glNormalPointer(GL_FLOAT, 24, (const void*)(sizeof(float)*3)); + + glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); + CheckErrors("drawarrays"); + } + + // draw wireframe + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonMode(GL_FRONT, GL_LINE); + glPolygonMode(GL_BACK, GL_LINE); + + glColor3f(0.0f, 0.0f, 0.4f); + for (size_t i = 0; i < drawObjects.size(); i++) { + DrawObject o = drawObjects[i]; + if (o.vb < 1) { + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, 24, (const void*)0); + glNormalPointer(GL_FLOAT, 24, (const void*)(sizeof(float)*3)); + + glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); + CheckErrors("drawarrays"); + } +} + +static void Init() { + trackball(curr_quat, 0, 0, 0, 0); + + eye[0] = 0.0f; + eye[1] = 0.0f; + eye[2] = 3.0f; + + lookat[0] = 0.0f; + lookat[1] = 0.0f; + lookat[2] = 0.0f; + + up[0] = 0.0f; + up[1] = 1.0f; + up[2] = 0.0f; +} + + +int main(int argc, char **argv) +{ + if (argc < 2) { + std::cout << "Needs input.obj\n" << std::endl; + return 0; + } + + Init(); + + + if(!glfwInit()){ + std::cerr << "Failed to initialize GLFW." << std::endl; + return -1; + } + + + + window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); + if(window == NULL){ + std::cerr << "Failed to open GLFW window. " << std::endl; + glfwTerminate(); + return 1; + } + + glfwMakeContextCurrent(window); + + // Callback + glfwSetWindowSizeCallback(window, reshapeFunc); + glfwSetKeyCallback(window, keyboardFunc); + glfwSetMouseButtonCallback(window, clickFunc); + glfwSetCursorPosCallback(window, motionFunc); + + glewExperimental = true; + if (glewInit() != GLEW_OK) { + std::cerr << "Failed to initialize GLEW." << std::endl; + return -1; + } + + reshapeFunc(window, width, height); + + float bmin[3], bmax[3]; + if (false == LoadObjAndConvert(bmin, bmax, gDrawObjects, argv[1])) { + return -1; + } + + float maxExtent = 0.5f * (bmax[0] - bmin[0]); + if (maxExtent < 0.5f * (bmax[1] - bmin[1])) { + maxExtent = 0.5f * (bmax[1] - bmin[1]); + } + if (maxExtent < 0.5f * (bmax[2] - bmin[2])) { + maxExtent = 0.5f * (bmax[2] - bmin[2]); + } + + while(glfwWindowShouldClose(window) == GL_FALSE) { + glfwPollEvents(); + glClearColor(0.1f, 0.2f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_DEPTH_TEST); + + // camera & rotate + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + GLfloat mat[4][4]; + gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]); + build_rotmatrix(mat, curr_quat); + glMultMatrixf(&mat[0][0]); + + // Fit to -1, 1 + glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); + + // Centerize object. + glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2])); + + Draw(gDrawObjects); + + glfwSwapBuffers(window); + } + + glfwTerminate(); +} From 00ed158a8eed7c50549c3f294759f5e55664ce58 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 12:32:12 +0900 Subject: [PATCH 113/585] Update CMake. --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4db707c7..f4f497ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,8 @@ set(tinyobjloader-Source ${CMAKE_CURRENT_SOURCE_DIR}/tiny_obj_loader.cc ) -set(tinyobjloader-Test-Source - ${CMAKE_CURRENT_SOURCE_DIR}/test.cc +set(tinyobjloader-Example-Source + ${CMAKE_CURRENT_SOURCE_DIR}/loader_example.cc ) set(tinyobjloader-examples-objsticher @@ -26,10 +26,10 @@ add_library(tinyobjloader ${tinyobjloader-Source} ) -option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Test Loader Application" OFF) +option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Example Loader Application" OFF) if(TINYOBJLOADER_BUILD_TEST_LOADER) - add_executable(test_loader ${tinyobjloader-Test-Source}) + add_executable(test_loader ${tinyobjloader-Example-Source}) target_link_libraries(test_loader tinyobjloader) endif() From f4695de408516f45652b161a34bb62fbe6edec07 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 12:50:41 +0900 Subject: [PATCH 114/585] Suppress compiler warning. --- tiny_obj_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index d2aae380..af395ff3 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -493,7 +493,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize, // Parse raw triples: i, i/j/k, i//k, i/j static vertex_index parseRawTriple(const char **token) { - vertex_index vi(0x80000000); // 0x80000000 = -2147483648 = invalid + vertex_index vi(-2147483648); // 0x80000000 = -2147483648 = invalid vi.v_idx = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); From a55247574c2c2436989a9a0654c51a62cc7193ba Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:11:55 +0900 Subject: [PATCH 115/585] Suppress VC2013 warnings. Update AppVeyor script. --- appveyor.yml | 11 +++-------- build.ninja | 18 ------------------ tests/config-msvc.py | 2 +- tests/tester.cc | 23 +++++++++++++++++++++++ tests/vcbuild.bat | 1 + tiny_obj_loader.h | 26 ++++++++++++-------------- 6 files changed, 40 insertions(+), 41 deletions(-) delete mode 100644 build.ninja diff --git a/appveyor.yml b/appveyor.yml index 38eaf158..b52a6666 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,12 +1,7 @@ version: 0.9.{build} -# scripts that runs after repo cloning. -install: - - vcsetup.bat - platform: x64 -configuration: Release -build: - parallel: true - project: TinyObjLoaderSolution.sln +build_script: + - cd tests + - vcbuild.bat diff --git a/build.ninja b/build.ninja deleted file mode 100644 index 5048ff1d..00000000 --- a/build.ninja +++ /dev/null @@ -1,18 +0,0 @@ -# build.ninja -cc = clang -cxx = clang++ -cflags = -Werror -Weverything -cxxflags = -Werror -Weverything -#cflags = -O2 -#cxxflags = -O2 - -rule compile - command = $cxx $cxxflags -c $in -o $out - -rule link - command = $cxx $in -o $out - -build loader_example.o: compile loader_example.cc -build loader_example: link loader_example.o - -default loader_example diff --git a/tests/config-msvc.py b/tests/config-msvc.py index 06fae62f..a7771def 100644 --- a/tests/config-msvc.py +++ b/tests/config-msvc.py @@ -32,7 +32,7 @@ cxxflags = { "gnu" : [ "-O2", "-g" ] - , "msvc" : [ "/O2" ] + , "msvc" : [ "/O2", "/W4", "/EHsc"] , "clang" : [ "-O2", "-g", "-fsanitize=address" ] } diff --git a/tests/tester.cc b/tests/tester.cc index edb92ba6..f16eeb09 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -300,6 +300,29 @@ TEST_CASE("cornell_box", "[Loader]") { REQUIRE(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath)); } +TEST_CASE("catmark_torus_creases0", "[Loader]") { + + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/catmark_torus_creases0.obj", gMtlBasePath, /*triangulate*/false); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + REQUIRE(true == ret); + + REQUIRE(1 == shapes.size()); + REQUIRE(8 == shapes[0].mesh.tags.size()); +} + +TEST_CASE("stream_load", "[Stream]") { + REQUIRE(true == TestStreamLoadObj()); +} + #if 0 int main( diff --git a/tests/vcbuild.bat b/tests/vcbuild.bat index 3dbc6c16..e8646735 100644 --- a/tests/vcbuild.bat +++ b/tests/vcbuild.bat @@ -1,3 +1,4 @@ chcp 437 +python kuroga.py config-msvc.py call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 ninja diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index af395ff3..5db2bb82 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -236,15 +236,6 @@ struct tag_sizes { int num_strings; }; -// for std::map -static inline bool operator<(const vertex_index &a, const vertex_index &b) { - if (a.v_idx != b.v_idx) return (a.v_idx < b.v_idx); - if (a.vn_idx != b.vn_idx) return (a.vn_idx < b.vn_idx); - if (a.vt_idx != b.vt_idx) return (a.vt_idx < b.vt_idx); - - return false; -} - struct obj_shape { std::vector v; std::vector vn; @@ -346,11 +337,13 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { } // Read the integer part. - while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { mantissa *= 10; mantissa += static_cast(*curr - 0x30); curr++; read++; + end_not_reached = (curr != s_end); } // We must make sure we actually got something. @@ -362,11 +355,13 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { if (*curr == '.') { curr++; read = 1; - while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { // NOTE: Don't use powf here, it will absolutely murder precision. mantissa += static_cast(*curr - 0x30) * pow(10.0, -read); read++; curr++; + end_not_reached = (curr != s_end); } } else if (*curr == 'e' || *curr == 'E') { } else { @@ -379,7 +374,8 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { if (*curr == 'e' || *curr == 'E') { curr++; // Figure out if a sign is present and if it is. - if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-')) { + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { exp_sign = *curr; curr++; } else if (IS_DIGIT(*curr)) { /* Pass through. */ @@ -389,11 +385,13 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) { } read = 0; - while ((end_not_reached = (curr != s_end)) && IS_DIGIT(*curr)) { + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { exponent *= 10; exponent += static_cast(*curr - 0x30); curr++; read++; + end_not_reached = (curr != s_end); } exponent *= (exp_sign == '+' ? 1 : -1); if (read == 0) goto fail; @@ -493,7 +491,7 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize, // Parse raw triples: i, i/j/k, i//k, i/j static vertex_index parseRawTriple(const char **token) { - vertex_index vi(-2147483648); // 0x80000000 = -2147483648 = invalid + vertex_index vi(static_cast(0x80000000)); // 0x80000000 = -2147483648 = invalid vi.v_idx = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); From 6b41e68bbc589cd2439845b5041e71ee5f60dd61 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:25:57 +0900 Subject: [PATCH 116/585] Install ninja in appveyor build. --- appveyor.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index b52a6666..22e3d7cf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,22 @@ version: 0.9.{build} platform: x64 +install: + ####################################################################################### + # All external dependencies are installed in C:\projects\deps + ####################################################################################### + - mkdir C:\projects\deps + - cd C:\projects\deps + + ####################################################################################### + # Install Ninja + ####################################################################################### + - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.6.0/ninja-win.zip" + - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip + - 7z x ninja.zip -oC:\projects\deps\ninja > nul + - set PATH=C:\projects\deps\ninja;%PATH% + - ninja --version + build_script: - cd tests - vcbuild.bat From 9eecb4634d98cfe0fa09b1caac01f602f13ec9cb Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:27:44 +0900 Subject: [PATCH 117/585] Update Travis script. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3ec9e45a..0b1926d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ matrix: --coverage" REPORT_COVERAGE=1 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi +- if [ -n "$REPORT_COVERAGE" ]; then pip install --user requests[security]; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi script: - cd tests From 28d1bb55218467b935436bf4e651deed33801d5b Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:32:03 +0900 Subject: [PATCH 118/585] Update Travis. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0b1926d7..c9403fdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ matrix: --coverage" REPORT_COVERAGE=1 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi +- if [ -n "$REPORT_COVERAGE" ]; then pip install --upgrade pip; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user requests[security]; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi script: From 016362234b0c28d010f9dfb70699bfb94eeb9e7d Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:36:46 +0900 Subject: [PATCH 119/585] Update Travis script. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c9403fdb..da93e43a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ matrix: --coverage" REPORT_COVERAGE=1 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi -- if [ -n "$REPORT_COVERAGE" ]; then pip install --upgrade pip; fi +- if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user requests[security]; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi script: From 170cb86870fe71224bc4c17c81a2e97f045150ac Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 13:56:27 +0900 Subject: [PATCH 120/585] Change to sudo required. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index da93e43a..78bbd0bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: cpp -sudo: false +sudo: required matrix: include: - addons: &1 From a66eab0f75dcc4c8f81c6adfc4c942114fafb221 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 14:03:43 +0900 Subject: [PATCH 121/585] More Travis fix. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 78bbd0bf..74ab5319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ matrix: --coverage" REPORT_COVERAGE=1 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi +- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get install python-dev libffi-dev libssl-dev; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user requests[security]; fi - if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi From 2e6cccbfe415814f6c788dc7db3998d3c3a129e9 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 14:32:10 +0900 Subject: [PATCH 122/585] Force use g++ for pip install. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74ab5319..a2a4428d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,8 +42,8 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get install python-dev libffi-dev libssl-dev; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi -- if [ -n "$REPORT_COVERAGE" ]; then pip install --user requests[security]; fi -- if [ -n "$REPORT_COVERAGE" ]; then pip install --user cpp-coveralls; fi +- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user requests[security]; fi +- if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user cpp-coveralls; fi script: - cd tests - make check From a608c3b5b1f867044ea99d1f7bc4847ea44ab01d Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 14:37:57 +0900 Subject: [PATCH 123/585] Update python. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a2a4428d..cc79cf5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ matrix: --coverage" REPORT_COVERAGE=1 before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi +- if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get update python; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get install python-dev libffi-dev libssl-dev; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo pip install --upgrade pip; fi - if [ -n "$REPORT_COVERAGE" ]; then CXX=g++ pip install --user requests[security]; fi From 28fc3523d46470a9c5806635277730ba3b243879 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 14:46:23 +0900 Subject: [PATCH 124/585] Disable coveralls due to Travis + python bug? --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc79cf5d..06b2d758 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,8 +36,7 @@ matrix: env: COMPILER_VERSION=4.9 BUILD_TYPE=Release - addons: *1 compiler: clang - env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0 --coverage" CXXFLAGS="-O0 - --coverage" REPORT_COVERAGE=1 + env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi - if [ -n "$REPORT_COVERAGE" ]; then sudo apt-get update python; fi From 7399aedfdd2097d8906dfc1760f8110ca257bd2e Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Tue, 19 Apr 2016 20:34:51 +0900 Subject: [PATCH 125/585] Initial support of linux for viewer example. --- examples/viewer/Makefile | 10 ++++++++-- examples/viewer/viewer.cc | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/viewer/Makefile b/examples/viewer/Makefile index e5204fca..9c1f621b 100644 --- a/examples/viewer/Makefile +++ b/examples/viewer/Makefile @@ -1,6 +1,12 @@ GLFW_INC=-I/usr/local/include -GLFW_LIBS=-L/usr/local/lib -lglfw3 -lglew -GL_LIBS=-framework OpenGL + +# OSX +#GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW +#GL_LIBS=-framework OpenGL + +# Linux +GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW +GL_LIBS=-lGL -lGLU -lX11 -lXrandr -lXi -lXxf86vm -lXcursor -lXinerama -ldl -pthread CXX_FLAGS=-Wno-deprecated-declarations diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc index d89fadc4..20221688 100644 --- a/examples/viewer/viewer.cc +++ b/examples/viewer/viewer.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -345,6 +346,7 @@ int main(int argc, char **argv) } glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Callback glfwSetWindowSizeCallback(window, reshapeFunc); From 58fa260605fe380387ac08e59bdbff87add72857 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 20 Apr 2016 16:00:45 +0900 Subject: [PATCH 126/585] Show normal vector in viewer example. --- README.md | 8 +++-- examples/viewer/Makefile | 12 +++++--- examples/viewer/viewer.cc | 42 +++++++++++++++++++------- loader_example.cc | 3 ++ tiny_obj_loader.h | 62 ++++++++++++++++++++++++++------------- 5 files changed, 88 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 35dfd1e2..838890f5 100644 --- a/README.md +++ b/README.md @@ -79,12 +79,13 @@ Features TODO ---- -* [ ] Read .obj/.mtl from memory +* [ ] Read .obj/.mtl from memory. +* [ ] Fix Python binding. License ------- -Licensed under 2 clause BSD. +Licensed under MIT license. Usage ----- @@ -94,11 +95,12 @@ Usage #include "tiny_obj_loader.h" std::string inputfile = "cornell_box.obj"; +tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; -bool ret = tinyobj::LoadObj(shapes, materials, err, inputfile.c_str()); +bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, inputfile.c_str()); if (!err.empty()) { // `err` may contain warning message. std::cerr << err << std::endl; diff --git a/examples/viewer/Makefile b/examples/viewer/Makefile index 9c1f621b..f06e3d0b 100644 --- a/examples/viewer/Makefile +++ b/examples/viewer/Makefile @@ -1,12 +1,16 @@ GLFW_INC=-I/usr/local/include -# OSX -#GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW -#GL_LIBS=-framework OpenGL +UNAME=$(shell uname -s) -# Linux +ifeq ($(UNAME),Darwin) +# OSX +GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW +GL_LIBS=-framework OpenGL +else +# Assume Linux GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW GL_LIBS=-lGL -lGLU -lX11 -lXrandr -lXi -lXxf86vm -lXcursor -lXinerama -ldl -pthread +endif CXX_FLAGS=-Wno-deprecated-declarations diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc index 20221688..a86c853b 100644 --- a/examples/viewer/viewer.cc +++ b/examples/viewer/viewer.cc @@ -30,8 +30,8 @@ typedef struct { std::vector gDrawObjects; -int width = 512; -int height = 512; +int width = 768; +int height = 768; double prevMouseX, prevMouseY; bool mouseLeftPressed; @@ -92,8 +92,11 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector& dr return false; } + printf("# of vertices = %d\n", (int)(attrib.vertices.size()) / 3); + printf("# of normals = %d\n", (int)(attrib.normals.size()) / 3); + printf("# of texcoords = %d\n", (int)(attrib.texcoords.size()) / 2); printf("# of materials = %d\n", (int)materials.size()); - printf("# of shapes = %d\n", (int)shapes.size()); + printf("# of shapes = %d\n", (int)shapes.size()); bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); @@ -101,7 +104,7 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector& dr { for (size_t s = 0; s < shapes.size(); s++) { DrawObject o; - std::vector vb; // pos(3float), normal(3float) + std::vector vb; // pos(3float), normal(3float), color(3float) for (size_t f = 0; f < shapes[s].mesh.indices.size()/3; f++) { tinyobj::index_t idx0 = shapes[s].mesh.indices[3*f+0]; @@ -150,6 +153,19 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector& dr vb.push_back(n[k][0]); vb.push_back(n[k][1]); vb.push_back(n[k][2]); + // Use normal as color. + float c[3] = {n[k][0], n[k][1], n[k][2]}; + float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; + if (len2 > 0.0f) { + float len = sqrtf(len2); + + c[0] /= len; + c[1] /= len; + c[2] /= len; + } + vb.push_back(c[0] * 0.5 + 0.5); + vb.push_back(c[1] * 0.5 + 0.5); + vb.push_back(c[2] * 0.5 + 0.5); } } @@ -160,7 +176,8 @@ bool LoadObjAndConvert(float bmin[3], float bmax[3], std::vector& dr glGenBuffers(1, &o.vb); glBindBuffer(GL_ARRAY_BUFFER, o.vb); glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); - o.numTriangles = vb.size() / 6 / 3; + o.numTriangles = vb.size() / 9 / 3; + printf("shape[%d] # of triangles = %d\n", static_cast(s), o.numTriangles); } gDrawObjects.push_back(o); @@ -243,8 +260,8 @@ void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){ add_quats(prev_quat, curr_quat, curr_quat); } else if (mouseMiddlePressed) { - eye[0] += transScale * (mouse_x - prevMouseX) / (float)width; - lookat[0] += transScale * (mouse_x - prevMouseX) / (float)width; + eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; + lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; } else if (mouseRightPressed) { @@ -274,8 +291,10 @@ void Draw(const std::vector& drawObjects) glBindBuffer(GL_ARRAY_BUFFER, o.vb); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - glVertexPointer(3, GL_FLOAT, 24, (const void*)0); - glNormalPointer(GL_FLOAT, 24, (const void*)(sizeof(float)*3)); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 36, (const void*)0); + glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); + glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6)); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); CheckErrors("drawarrays"); @@ -296,8 +315,9 @@ void Draw(const std::vector& drawObjects) glBindBuffer(GL_ARRAY_BUFFER, o.vb); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - glVertexPointer(3, GL_FLOAT, 24, (const void*)0); - glNormalPointer(GL_FLOAT, 24, (const void*)(sizeof(float)*3)); + glDisableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 36, (const void*)0); + glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); CheckErrors("drawarrays"); diff --git a/loader_example.cc b/loader_example.cc index d91baaa6..435684ee 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -1,3 +1,6 @@ +// +// g++ loader_example.cc +// #define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 5db2bb82..8500fc91 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -1,11 +1,30 @@ -// -// Copyright 2012-2016, Syoyo Fujita. -// -// Licensed under 2-clause BSD license. -// +/* +The MIT License (MIT) + +Copyright (c) 2012-2016 Syoyo Fujita and many contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ // -// version devel : Change data structure. Support different index for +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// Support different index for // vertex/normal/texcoord(#73, #39) // version 0.9.20: Fixes creating per-face material using `usemtl`(#68) // version 0.9.17: Support n-polygon and crease tag(OpenSubdiv extension) @@ -117,8 +136,9 @@ typedef struct callback_t_ { void (*texcoord_cb)(void *user_data, float x, float y); // -2147483648 will be passed for undefined index void (*index_cb)(void *user_data, int v_idx, int vn_idx, int vt_idx); - // `name` material name, `materialId` = the array index of material_t[]. -1 if a material not found in .mtl - void (*usemtl_cb)(void *user_data, const char* name, int materialId); + // `name` material name, `materialId` = the array index of material_t[]. -1 if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int materialId); // `materials` = parsed material data. void (*mtllib_cb)(void *user_data, const material_t *materials, int num_materials); @@ -126,17 +146,16 @@ typedef struct callback_t_ { void (*group_cb)(void *user_data, const char **names, int num_names); void (*object_cb)(void *user_data, const char *name); - callback_t_() : - vertex_cb(NULL), - normal_cb(NULL), - texcoord_cb(NULL), - index_cb(NULL), - usemtl_cb(NULL), - mtllib_cb(NULL), - group_cb(NULL), - object_cb(NULL) { - } - + callback_t_() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} + } callback_t; class MaterialReader { @@ -491,7 +510,8 @@ static vertex_index parseTriple(const char **token, int vsize, int vnsize, // Parse raw triples: i, i/j/k, i//k, i/j static vertex_index parseRawTriple(const char **token) { - vertex_index vi(static_cast(0x80000000)); // 0x80000000 = -2147483648 = invalid + vertex_index vi( + static_cast(0x80000000)); // 0x80000000 = -2147483648 = invalid vi.v_idx = atoi((*token)); (*token) += strcspn((*token), "/ \t\r"); @@ -1179,7 +1199,7 @@ bool LoadObjWithCallback(void *user_data, const callback_t &callback, // material std::map material_map; - int materialId = -1; // -1 = invalid + int materialId = -1; // -1 = invalid int maxchars = 8192; // Alloc enough size. std::vector buf(static_cast(maxchars)); // Alloc enough size. From 9d6b58b90e879a87e4455f79ef9ae585f9bfed10 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 24 Apr 2016 00:34:05 +0900 Subject: [PATCH 127/585] Add new build.ninja. --- README.md | 1 + build.ninja | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 build.ninja diff --git a/README.md b/README.md index 838890f5..f24bc8d5 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ TODO * [ ] Read .obj/.mtl from memory. * [ ] Fix Python binding. +* [ ] Unit test codes. License ------- diff --git a/build.ninja b/build.ninja new file mode 100644 index 00000000..77801bb5 --- /dev/null +++ b/build.ninja @@ -0,0 +1,53 @@ +ninja_required_version = 1.4 + +gnubuilddir = build +gnudefines = +gnuincludes = -I. +gnucflags = -O2 -g +gnucxxflags = -O2 -g -pedantic -Wall -Wextra -Wcast-align -Wcast-qual $ + -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self $ + -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast $ + -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion $ + -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror $ + -Wno-unused -fsanitize=address +gnuldflags = -fsanitize=address + +pool link_pool + depth = 1 + +rule gnucxx + command = $gnucxx -MMD -MF $out.d $gnudefines $gnuincludes $gnucxxflags $ + -c $in -o $out + description = CXX $out + depfile = $out.d + deps = gcc +rule gnucc + command = $gnucc -MMD -MF $out.d $gnudefines $gnuincludes $gnucflags -c $ + $in -o $out + description = CC $out + depfile = $out.d + deps = gcc +rule gnulink + command = $gnuld -o $out $in $libs $gnuldflags + description = LINK $out + pool = link_pool +rule gnuar + command = $gnuar rsc $out $in + description = AR $out + pool = link_pool +rule gnustamp + command = touch $out + description = STAMP $out + +gnucxx = g++ +gnucc = gcc +gnuld = $gnucxx +gnuar = ar + +build loader_example.o: gnucxx loader_example.cc + + +build loader_example: gnulink loader_example.o +build all: phony loader_example + +default all From 7dc1418e2b1826a7859393d4c0f08d2ece7808d5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Apr 2016 19:42:42 +0200 Subject: [PATCH 128/585] viewer: Cleaned up Makefile and added a semblance of a Mingw64-target for Windows. --- examples/viewer/Makefile | 64 +++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/examples/viewer/Makefile b/examples/viewer/Makefile index f06e3d0b..d42278c3 100644 --- a/examples/viewer/Makefile +++ b/examples/viewer/Makefile @@ -1,18 +1,66 @@ -GLFW_INC=-I/usr/local/include +CXX_FLAGS=-O0 -g -Wno-deprecated-declarations +ifndef PLATFORM UNAME=$(shell uname -s) +# OSX TEST ifeq ($(UNAME),Darwin) -# OSX -GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW +PLATFORM=OSX +else +# WINDOWS MINGW TEST +ifeq ($(findstring MINGW, $(UNAME)), MINGW) + PLATFORM=WINDOWS_MINGW +else +# ASSUME LINUX + PLATFORM=LINUX +endif +endif + +endif +$(info PLATFORM SELECTED: $(PLATFORM)) + +ifeq ($(PLATFORM),OSX) +# OSX CONFIGURATION------------------------------------------------------------- +CXX_FLAGS+= -fsanitize=address +GLFW_INC=-I/usr/local/include +GLFW_LIB_DIR=/usr/local/lib +GLFW_LIBS=-L$(GLFW_LIB_DIR) -lglfw3 -lGLEW GL_LIBS=-framework OpenGL +# /OSX CONFIGURATION------------------------------------------------------------ else -# Assume Linux -GLFW_LIBS=-L/usr/local/lib -lglfw3 -lGLEW +ifeq ($(PLATFORM),LINUX) +# LINUX CONFIGURATION----------------------------------------------------------- +CXX_FLAGS+= -fsanitize=address +GLFW_INC=-I/usr/local/include +GLFW_LIB_DIR=/usr/local/lib +GLFW_LIBS=-L$(GLFW_LIB_DIR) -lglfw3 -lGLEW GL_LIBS=-lGL -lGLU -lX11 -lXrandr -lXi -lXxf86vm -lXcursor -lXinerama -ldl -pthread +# /LINUX CONFIGURATION---------------------------------------------------------- +else +ifeq ($(PLATFORM),WINDOWS_MINGW) +# WINDOWS MINGW CONFIGURATION--------------------------------------------------- +# Since Windows unfortunately doesn't have any defacto directories for third +# party libraries, we'll have to force the user to give us them. So we check +# that the user passed in GLFW-dirs in some way, via flag or environ. +ifndef GLFW_DIR +$(error NO GLFW_DIR SPECIFIED. PASS GLFW_DIR= TO MAKE) +endif +ifndef GLEW_DIR +$(error NO GLEW_DIR SPECIFIED. PASS GLEW_DIR= TO MAKE) endif - -CXX_FLAGS=-Wno-deprecated-declarations +GLFW_INC=-I$(GLFW_DIR)/include +GLFW_LIB_DIR=$(GLFW_DIR)/lib-mingw-w64 +GLFW_LIBS= -lglfw3 -L$(GLFW_LIB_DIR) +GL_LIBS= -lglew32 -lopengl32 -lglu32 -lgdi32 -I$(GLEW_DIR)/include -L$(GLEW_DIR)/bin/Release/x64 +# /WINDOWS CONFIGURATION-------------------------------------------------------- +else +$(error UNKNOWN PLATFORM "$(PLATFORM)") +endif #ifeq ($(PLATFORM),WINDOWS_MINGW) +endif #ifeq ($(PLATFORM),LINUX) +endif #ifeq ($(PLATFORM),OSX) all: - g++ -fsanitize=address -O0 -g -o objviewer $(CXX_FLAGS) viewer.cc ../../tiny_obj_loader.cc trackball.cc $(GLFW_INC) $(GL_LIBS) $(GLFW_LIBS) + g++ -o objviewer $(CXX_FLAGS) viewer.cc ../../tiny_obj_loader.cc trackball.cc $(GLFW_INC) $(GLFW_LIBS) $(GL_LIBS) +ifeq ($(PLATFORM), WINDOWS_MINGW) + cp $(GLEW_DIR)/bin/Release/x64/glew32.dll . +endif From 8d60b4963ae7a7cccd41d2bd22f2b2e17f1e0e9d Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 1 May 2016 21:30:50 +0900 Subject: [PATCH 129/585] Rename varialble for better understanding. Write some API usage in README.md. --- README.md | 36 +++++++++++++++++++++++++++++++++--- loader_example.cc | 10 +++++----- tests/tester.cc | 8 ++++---- tiny_obj_loader.h | 6 +++--- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f24bc8d5..74bc8f81 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,9 @@ Features TODO ---- -* [ ] Read .obj/.mtl from memory. * [ ] Fix Python binding. -* [ ] Unit test codes. +* [ ] Fix obj_sticker example. +* [ ] More unit test codes. License ------- @@ -91,6 +91,10 @@ Licensed under MIT license. Usage ----- +`attrib_t` contains single and linear array of vertex data(position, normal and texcoord). +Each `shape_t` does not contain vertex data but contains array index to `attrib_t`. +See `loader_example.cc` for more details. + ```c++ #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc #include "tiny_obj_loader.h" @@ -111,7 +115,33 @@ if (!ret) { exit(1); } -// See loader_example.cc for more details. +// Loop over shapes +for (size_t s = 0; s < shapes.size(); s++) { + // Loop over faces(polygon) + size_t index_offset = 0; + for (size_t f = 0; f < shapes[i].mesh.num_face_vertices; f++) { + int fv = shapes[i].mesh.num_face_vertices[f]; + + // Loop over vertices in the face. + for (size_t v = 0; v < fv; f++) { + // access to vertex + tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; + float vx = attrib.positions[3*idx.vertex_index+0]; + float vy = attrib.positions[3*idx.vertex_index+1]; + float vz = attrib.positions[3*idx.vertex_index+2]; + float nx = attrib.normals[3*idx.normal_index+0]; + float ny = attrib.normals[3*idx.normal_index+1]; + float nz = attrib.normals[3*idx.normal_index+2]; + float tx = attrib.texcoords[2*idx.texcoord_index+0]; + float ty = attrib.texcoords[2*idx.texcoord_index+1]; + } + index_offset += fv; + + // per-face material + shapes[i].mesh.material_ids[f]; + } +} + ``` diff --git a/loader_example.cc b/loader_example.cc index 435684ee..e592e955 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -65,7 +65,7 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(shapes[i].mesh.num_vertices[v])); + printf("shape[%ld].num_faces: %ld\n", i, shapes[i].mesh.num_face_vertices.size()); + for (size_t v = 0; v < shapes[i].mesh.num_face_vertices.size(); v++) { + printf(" num_face_vertices[%ld] = %ld\n", v, + static_cast(shapes[i].mesh.num_face_vertices[v])); } //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); diff --git a/tests/tester.cc b/tests/tester.cc index f16eeb09..e8e3b42b 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -65,7 +65,7 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(shapes[i].mesh.num_vertices[v])); + static_cast(shapes[i].mesh.num_face_vertices[v])); } //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 8500fc91..01ff721d 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -113,7 +113,7 @@ typedef struct { typedef struct { std::vector indices; std::vector - num_vertices; // The number of vertices per face. Up to 255. + num_face_vertices; // The number of vertices per face. 3 = polygon, 4 = quad, ... Up to 255. std::vector material_ids; // per-face material ID std::vector tags; // SubD tag } mesh_t; @@ -604,7 +604,7 @@ static bool exportFaceGroupToShape( shape->mesh.indices.push_back(idx1); shape->mesh.indices.push_back(idx2); - shape->mesh.num_vertices.push_back(3); + shape->mesh.num_face_vertices.push_back(3); shape->mesh.material_ids.push_back(material_id); } } else { @@ -615,7 +615,7 @@ static bool exportFaceGroupToShape( idx.texcoord_index = face[k].vt_idx; } - shape->mesh.num_vertices.push_back(static_cast(npolys)); + shape->mesh.num_face_vertices.push_back(static_cast(npolys)); shape->mesh.material_ids.push_back(material_id); // per face } } From 368312cb4bd95e2806da09a2d2f43d04de73cb41 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 2 May 2016 01:41:37 +0900 Subject: [PATCH 130/585] Fix index buffer was not filled when !triangulate case. Suppress VS2015 warnings. Update premake5.exe. --- loader_example.cc | 76 +++++++++++++------------------------ tiny_obj_loader.h | 1 + tools/windows/premake5.exe | Bin 514560 -> 912896 bytes 3 files changed, 28 insertions(+), 49 deletions(-) diff --git a/loader_example.cc b/loader_example.cc index e592e955..c44e0e76 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -11,7 +11,7 @@ #include #include -static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials, bool triangulate = true) +static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials) { std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; @@ -21,76 +21,54 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(v), static_cast(attrib.vertices[3*v+0]), static_cast(attrib.vertices[3*v+1]), static_cast(attrib.vertices[3*v+2])); } for (size_t v = 0; v < attrib.normals.size() / 3; v++) { - printf(" n[%ld] = (%f, %f, %f)\n", v, + printf(" n[%ld] = (%f, %f, %f)\n", static_cast(v), static_cast(attrib.normals[3*v+0]), static_cast(attrib.normals[3*v+1]), static_cast(attrib.normals[3*v+2])); } for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { - printf(" uv[%ld] = (%f, %f)\n", v, + printf(" uv[%ld] = (%f, %f)\n", static_cast(v), static_cast(attrib.texcoords[2*v+0]), static_cast(attrib.texcoords[2*v+1])); } + // For each shape for (size_t i = 0; i < shapes.size(); i++) { - printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("shape[%ld].name = %s\n", static_cast(i), shapes[i].name.c_str()); + printf("Size of shape[%ld].indices: %lu\n", static_cast(i), static_cast(shapes[i].mesh.indices.size())); - if (triangulate) - { - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - tinyobj::index_t i0 = shapes[i].mesh.indices[3*f+0]; - tinyobj::index_t i1 = shapes[i].mesh.indices[3*f+1]; - tinyobj::index_t i2 = shapes[i].mesh.indices[3*f+2]; - printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, - i0.vertex_index, i0.normal_index, i0.texcoord_index, - i1.vertex_index, i1.normal_index, i1.texcoord_index, - i2.vertex_index, i2.normal_index, i2.texcoord_index, - shapes[i].mesh.material_ids[f]); - } - } else { - for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { - tinyobj::index_t idx = shapes[i].mesh.indices[f]; - printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, idx.texcoord_index); - } + size_t index_offset = 0; - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert(shapes[i].mesh.material_ids.size() == shapes[i].mesh.num_face_vertices.size()); - for (size_t m = 0; m < shapes[i].mesh.material_ids.size(); m++) { - printf(" material_id[%ld] = %d\n", m, - shapes[i].mesh.material_ids[m]); - } + assert(shapes[i].mesh.num_face_vertices.size() == shapes[i].mesh.material_ids.size()); - } + printf("shape[%ld].num_faces: %lu\n", static_cast(i), static_cast(shapes[i].mesh.num_face_vertices.size())); - printf("shape[%ld].num_faces: %ld\n", i, shapes[i].mesh.num_face_vertices.size()); - for (size_t v = 0; v < shapes[i].mesh.num_face_vertices.size(); v++) { - printf(" num_face_vertices[%ld] = %ld\n", v, - static_cast(shapes[i].mesh.num_face_vertices[v])); - } + // For each face + for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) { + size_t fnum = shapes[i].mesh.num_face_vertices[f]; - //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - //assert((shapes[i].mesh.positions.size() % 3) == 0); - //for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { - // printf(" v[%ld] = (%f, %f, %f)\n", v, - // static_cast(shapes[i].mesh.positions[3*v+0]), - // static_cast(shapes[i].mesh.positions[3*v+1]), - // static_cast(shapes[i].mesh.positions[3*v+2])); - //} + // For each vertex in the face + for (size_t v = 0; v < fnum; v++) { + tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; + printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast(f), static_cast(v), idx.vertex_index, idx.normal_index, idx.texcoord_index); + } - printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size()); + printf(" face[%ld].material_id = %d\n", static_cast(f), shapes[i].mesh.material_ids[f]); + + index_offset += fnum; + } + + printf("shape[%ld].num_tags: %lu\n", static_cast(i), static_cast(shapes[i].mesh.tags.size())); for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { - printf(" tag[%ld] = %s ", t, shapes[i].mesh.tags[t].name.c_str()); + printf(" tag[%ld] = %s ", static_cast(t), shapes[i].mesh.tags[t].name.c_str()); printf(" ints: ["); for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) { @@ -128,7 +106,7 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(i), materials[i].name.c_str()); printf(" material.Ka = (%f, %f ,%f)\n", static_cast(materials[i].ambient[0]), static_cast(materials[i].ambient[1]), static_cast(materials[i].ambient[2])); printf(" material.Kd = (%f, %f ,%f)\n", static_cast(materials[i].diffuse[0]), static_cast(materials[i].diffuse[1]), static_cast(materials[i].diffuse[2])); printf(" material.Ks = (%f, %f ,%f)\n", static_cast(materials[i].specular[0]), static_cast(materials[i].specular[1]), static_cast(materials[i].specular[2])); @@ -179,7 +157,7 @@ TestLoadObj( return false; } - PrintInfo(attrib, shapes, materials, triangulate); + PrintInfo(attrib, shapes, materials); return true; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 01ff721d..2a931194 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -613,6 +613,7 @@ static bool exportFaceGroupToShape( idx.vertex_index = face[k].v_idx; idx.normal_index = face[k].vn_idx; idx.texcoord_index = face[k].vt_idx; + shape->mesh.indices.push_back(idx); } shape->mesh.num_face_vertices.push_back(static_cast(npolys)); diff --git a/tools/windows/premake5.exe b/tools/windows/premake5.exe index c0bf928d0ab4b377eed48ef1d0162e97d5d1eaeb..51c05a80b452f74326b5bd768397c94ca3ff830c 100644 GIT binary patch literal 912896 zcmeFadw5jU_4qx>Op*ZxW`FU!*WUcK-`Z9wYO4vM1iYfQiXs-3>W&9BRtiC)^M2MoGYQ&$zwh(B z@ALlm@;u?3v#)Efz1G@mueJ7``pa*1I2{g$i@$8v;n>bo{sr{^|NDOj$sTj+Lt`BG zk9hL5?FEaUJngdhm#?o_wfbvcT7BhLD!zE-SHJqTWW^V*u2`M=YQ>koS}}h~Q^i-l zcGcBqj~+d;GS~FQ_ct%R;cpjA&j0m&|F@Gj@_l9a2a{v^`K!r(p4b28p2^qga_3Ke zRj1!Q+3{B?ckkpI`M&haUyMr|pM*FPbT}3l6glp@^YLgt@1UcwU{t|Khht}v!*Qlt zC%^SPSn%%soc8vjVfla!gl$8qlIrNZ3$IkDKcC3?^ z{?U&6?WW}Gq<>=^2hTt0^M81a+&22-a{`=K0pI`h@9gB&>yl))_b?uTOU5dbeA2%P zhvSN~S6_8y@=Ax}!rl^l?7MmH790%!3xJTb?P886&LjQ4(GEu$&s%w(^e^CWbe+BW z`qf_~O^=l^3l1H>{I_y}vsYhz?bj&io+k}})A>B^{kL*+CHMdT_x~LRmYRnX9`)8P zr=w<1B%5*@ZX@h6!fv&P39ji%@AL##^iZhL40>W_yDQ@Rt8mndjbiC(9px!DK~c4m_kV%atBi24~xyr>C0(>r};aL_o#2)UPM zQyw$qc7|MZ9Z?&}r5`#i2)SDK=zgQIOmHxgD5QY;*_j27L;-K+fI2<_ft4CG`(tWF zHk(cFEN`vpYWY|lhh6JOC)MhqR(fZd*$-%=s2)=lRBmog6vpp+E$C3=^w&^JQQe!$ zP|NHW_?LpHs9M7RjDr3&ZtOyF3N<&IVOQs#Fj}aU&=DDuvDvhWM{^E2P z^W1uh3}*$COJSJZ5@;s2QROT=jEfA z;Z!#wArgfo?ab%xATPahzGTmHspXRIB>x`rd9>wf&hw~%WVuK)?5r#ESw3|JS>`-f zdS{@sD|xEfpD2Fxeg?}J-~TxyON}t=JpsmG6fj77Qb)X02p&lX2TPlq{S4{mbD59U zmYS~RdSY|a@ey5^{m@Nwb7xok)TUTX*4$@?JlW(dxBA0Q=BC%Ps5$j^>+ISVc zwG6rg*$eZeHw-#bo{&E^u;#BdU9B(@lxQ7G=utnQN9|$Mha&K&`ol-?jP(*?A$_>Q zCeyggY|YVFNPS)i0F=$4GrRg4s^@weM{n^bC>m2UDF@k1kz&C18NuW}DYoLEvRDX& zvgg-2lVfUwgUJ!K^IYD|Ut$4`@1%D8oqbvAn9NW7Pkc)Q?R?a9)$D=2sbK{BL6i|Z zY}6}Zh=XRZ-#^do&5WQ8bGgU4;&9ELnxjVjfKh+M1V^-=c?F6vuXj6BN6e7R-<`ao zw!xkB)YcECN@|Yw{g&Jav=5*%zANbu_}737J-9>ayd2aOwIO%XYt|p~H+WJdy+I}G zer@oOxAOsBYWCEIJTi&zSy#|>vb?szlbj@?Y?r$HLl6s^-AtX4Qr0Cgb(0{E?!Jj? zBLbs>J_`l^luNbHvwFm1m$+7!@R9to?x72j6&y$hkCfKz@xSNYJdsJy2K&>&!yZ06 z6^+QOC*A1{%8@FpIZDfaU~n)?bD&iyEb(DXT|w)R=^Bsqdp+NtCUb>58cX<^)DP*< z9H==Of0-$0GA=OIy3}nG3jh-!lBdW2D#>aw1Bk}&9T(tzuMFnHCbbsaL3D|an$#a< z{#i605Z>>I0d9 zCbeypO*G}TAy@ktv%y_E$CWJZEz#I7PCB!7E+!h`;_(NMwEaPZe86U-nyyGhOXab3 z5Y!RC7J>ZT2{M#2Tg2;>i;=m=gHiFqNdZUpyeoFqJJc&78H!yhy}|8BPNr_${W^=u zp~n$LU4e*t`Y~BAJS=u`mt;Ji&)7zW`g8tku`cG*9i}&U9LYI}tN_xXrvQ|mBm5*O zm6~Z`$W+mGA1y|*=Q-{Jn01=3rNpmLZ}md!(al9oEQKK)N5V+Trh6GbXdS z=mh0EW{pf0ZYh%?RdoDWTDT}RDbx8OFOm2vAc(0}d-5%>-4G(F55c2V2eW>#=E>#C z0wh}#YK)l!3F>^0T~4Avy(K)qQ|QK>-kt6HFT_za-XY^Eh^ZP)7!hcUj~#k?Jv6>z zvgT+QE3llruqt%t1k9G4CioK}@uq0aeo>2+M{{2xzQ|#?(JV*M&s%pj9JWuAa>$ zsVmVSQG)5awdKEL7(*%!jGA8QlF3^7Fp7&&49%p<#Wah}N36+}csKK~i5 z{D<5c?7af^%c3re9Q?zm9N5)>Z8mtC;A`jZMs*V)qs~E@yIds>qutZmV)ob!yrQq~ zS*JrbAfQ$Dvdt(7&Sgx$e_va(tovV(i4(;qIWij@tX(nK-e(13Y6;UOG#1s<>9Iaz z>mpy6!WQDAn7L2gL}N|h>$hVHCB-OKRk~;;fIpX4uhLf!I_w3n=4iqZVzoJq2C}JA z*r7?iSqzRnx{70FqJXN?&vpYlI^;CE-+`W$?YO%F{nH*P(v@ zoJ{R`lBr%M-Q3r?-@AD~?3hgr#K%noGIf;nXd=?2-qtTOo75rwQq`pP>zB_qsb_h~ z)cGRuDN;qb`~*RV63Ne!xjtC){6*GZYXDZ|LG*4brd%9Fm>kraFaN`%B{e1UF%z7&3SYQPa+MuSai zrmj#4CqtrUk5(y|$xVk#LH$WGSr#X_I*$3tfv96SZ;BXqALeP?-LL;YqW>S@|1Eb9 z^1r$RqDT}rWjdu4LU@U=;6SER3P#lZHf#D%q$WpI2w@@`4Z@0@1k&+IMD(7Xm|7-G z#)v8!O-dww2%<%{oZVBrj=1j- z;#g&%FLMC+K*7ZL0ON_Ooq&I!sGs+~?|^9J?hy9IB07Q1=sZL_U}SPc`I)=y1M;0~ zeKrZr&FG$BzPa{p=`lGQbnuL1AJ72ok|O^BeSaS;7)IPhfPo;fM#m3pus+LobMt;Z zgvLfKgnl7xRgZA=Fg|Z9+V9N8hrgl@A8uTtbbSXH1pC@^HP^Nu4=b;+uRcW?u~Bep3MBmD6Ly* z=`rW}(p^{ZTv%!@aG4{_1)j{@Wrn*q;AXZQEj^jJVRFNz{_yma%M65j%sDR8VFo;6 zsW6v{lr`tLqvp9rwYl15ta6!`x{ZiCJ;&{{B{5}00|}f8zs-yJ_img6>7%Kd$E(^s z-C39O1OLFr&vriBUS)>L%@4ZYbX9fxdpC}_G^&;@55Zb?7Q!jjFasdC;X%&^yNs131#5Aw+0QRn75q54h}h>qTqp+_SE-`&E}U$^>g) za68fgx6h1toX^xo+^a4920mBxP$u2&D+u!{m=AhHgqY8({X)oHK0nwwB@^y9ea2Wb z;xZatW@DMr=q8J5u9C4Mj6Z?9A0fX? zpG;RCX91yxljaOHVyRoLrQAircBvwivlTs?OhC%WLIZ(amdz8m>{Wn zk%;AW?&8#F)6uc6*pV#K)~=eae0>U~5Bt;vyPPvQE>qu+SYG+A;EZLtP+KLmX3IJz z+nlQrv9kEPk|WSglP+m1Z$h(sjjCM;a$3e>6!fVF`FK#E_rI6)A|OT~%OCodv=1xg zj70g0nUgDaoK$f<6}^$D0I=bRM(HOj%P<!ZspL!qtlt3hxdLR;Xy1ZD^ z^BYy4mqKEgODkvc^l-8)oE#Mnr>2?3X255ij*M%W zV{CX&A{Sbb={<>DRNrwu{sjw?FlUiX00J2sa`R)v*F`}_@6H!FxsiK@8kv~xb0$YG zNRC{P9JOG$=!(9r5SkV>h(CAwN?SDL+Hi}49d;j>jF>}T&{8X(wO?oDG7d}@fu^(c zoM@RD%L&cjwt|Oj%4fE_A?~QU>Cc+7jFR3uw<{++bNgs94p2};@ky{SQTC>C^B&n$ zvT4>Vatn~d^q=T8*Xf11PIZsnx9A>iNj3~%uBcavW`aIxH>&RVU(3GULRsutdj~{2 zNcp1b3lUky$|vI8Dt!iNL0&uKgp*ekjg6XTYnOV+6Pu6sjvdC>A%xr5V&O8zlFz36W77m8 zhgK=pH>pKab5?n8-(l{^&1HU>SaS@L1HA73#zr{lEa(^IX=pKl_4Nt>(=3*f129-7 zT(H#_z!ZGEs@ulLuO3F%U&A)GKNIYa#G<{y0hwg=@?mMMT%@iTVL!IIeR$OFxX;&^ ztybkPfKeZ73Fv6-*UQ`yWU(=4UQpjs-Jn~{l$C9>? z-=7?T({3%@SDc)-AX&K}IYNZY6>f+18JdcObF0>3S?L22RxJzhL?Tj#$rd2-F$^mo zL;Ax1`Ip9Mrm_FhboW7-&1o9{J_;Wk0^+0@y+JDc=NWzBLGixW($5$9_^du-Rt;-IMfd1?N1|p;esE$`ou`uIbCF$d{R< z%P^z5%m|9yLJ`J_9)qvq3H@HAk=~Ywl>qP!*~$cZ<3m za%V0r!1Cqme$7?2gRMR}jgZ+uB>t7pvOhOA461*Hr1#P^J~Mhp{iFBmr1$IGHr1sM zm!>zAI>aea`UGkkpl5=I5$Fgd?93mE{h^c;{dr!Uv{r7DtJc?1FpE>CLVTjVr6wk% zzN|8X+NQEl`}73)+feUhiXm(gP*ln>K^|Y-Q)D&jlR4p#Z9I)>>ULzW?oJM<~ zwjr?gye1W2=5TbhPjECRN1CC4KQw=hWu1cM+nOvjaU+BxmgqcY`(;MxG7OAQXJt;& zhBu!baAf$qgTEj0_jCS!$DhUDQT~v{f~AXR0ot$cuLa&idC`5 z9m^%OV+$1V*m=^~!nO-!&vNp%={GT2W}>S?vMs_E8CBoXg2R*Wu@zia)my-hZ(3(I z^=YZv?oK$g_c*GSKcKt&PrAaeJkPD;b-I^bY94Ac_3%uJJOZ+=FeopVh-A3{DKj1OxF&ll7JJ~%gyc6$$b_})2S2`}5kZO?+%{FSxHCE4 z9F^Z9#gq%|fQVLe*RYFK_H)>gz!&zrng7W9Ot3+!5hyqi7d2`(47Pn)93;Q^C$7n; z`Y{iDMFmmyG>W|#AEW9kh{*KL=`b$?jis{%?Gx0azsp&P|MVuN(y9lAxDFxdWGwIyFoEb8jW{o*cZZkEL*ek%<{)r%j%EpJ1!_=sfyiIHN) z{Z?^-HVi?4BU7KV9YSXm6*vHRJpj`_kTvslF0w}(W2Ue;F+8)^THM4a7|*Gcmy~@% z#6{_BWxFrFn?9xeCa2ERF&jm1f8X*{X;f_o1#l1k#XUWkYsvF&!(erp zIO!36?BxeLW;s&N8yg1pgQo2I;+KoMXgp^cx>;k;WBr+d;mS|B#XZjs4v+v1tM2ch z2LbX$W((oPXin+e-#(=&sxCV&Hvgz9AxG+5C%ep@Wl>ehOLJ~JEQBeJfw3-W5Kjae zoJLA&L`)5WlV-~oe^~}g<}^^u$YeBH%Gr3P>8F{hM3DeApzhTeDV0!|bQV=8?ol?X zrWa~dC}U~a)#ImlAp z_~-2c*9;ekFC!_c3eE$)(D-vq0BaC)>1~Qv>a2zVU4J>8So377s{cdIJ7v$GIuD*5 zu4yAcY#Qt*Y0Gj;-aAmC_HgB7sLXtkU}h0m#LM&^_FwrtD>wbW)=*S{%yVVVuL21# zzVlLFbF+4T-0-QWazRPLRv)}=KOfRFU5Y|FLy|)@pluFgw zX{z=}unL0~0pnTyl%A8cg9fo2(!sR;@d`pAYHO~_k8;oc!@xs&p{DfzRAX7L@Y z%5WzwW3Ui{l2L(>+7>@zBEgg`!P5WE`VpYXvlKM0`T zKEZ2L-2=a+ch1ahM3^VAEp4u~OS;uJ^i-2_gDzPyB;4Zt%%7mz^)FkNKCDqLkiRAD z#Mu9;0hypRD310~CZVxN19hEqOyy`0%aTo4DhTQk<_*wciuhLILS z%klKt{r>eM`Yi_62-Ze)g*|YF?Jyj(mQA8>?IQQg}~4wW%EEq zSt?RgY>LV%eh0H^Qw+xI3G6j-EXtrJFsP`iryN4JDma)^3G#$`7=2mh{i}MuSvgau zM&e~TP(MR2k$5RzwD7(Nz;cV$9W<76%i2)IY@$f(wgk8D*y94gEubmA6Ct*7q;&Ib zx!Z2M?t`HnYjRHw$rXXxIa{->Z+d*9|YNkf0v zROa!o7;Jlob^RZrB-jcS;XVph*+0+hZ;BZxWknhU4+hrMsRSwbNLEvRkKF|=epTpjm<5Ea{)A}en%nX>M*qk1}~!(dH#${8Sf;D zq9^Dn{np1C+X?hi!fxy;-Pr8m#&Qd)i}vE{fFA62f=s=`jxC|A1tgDRDl}79<}w>{)mG3v z%O)gzIvt~Ab?!%X*bJD|sP<<^FxIn1am0^mC7 z2@`eXw5G5eSf3F!McDNxr)kQ<-=zir*r>YgAK9#k;q<|en$ulQt$?T}@6GEUd2R1` z;I-*C<%|CqFl7bYVrwlibuGdwJF_#4PDaRz%@D&Hm;{u@R9MO&^7bJ#4#|*FWAOPL zmLaPK?Y~H$C`eAp&b(XNiSN}JcjUNhch!ArAj z)$|Hw6FhrwTGg5`^cREx7>p>xA{$rnsoV9&J#T4h{OM79v>174JqGRH|C&@O9VSLu z8}9}MgfFeUIDpN=t=tfdXiz>|gOa3aG8TE+TCH2uAj$XlDJv`A2>Eyrh7z?==ZgKL zRHuRuS%WdAxYe)uU|Vjl^EYayu3XX66n#YN#4%L|oDW|C^oVS=ocwqP`Wb@tArX5e z@nteFbr&7Q7xPM?^QF+IFCZ>srTjNv7igmwqD11`&L^o~{+q4SuhJuxX?|C@n8|pa zkqP=oBL#?)pQVe{O8eEkO3vRj9nS|rFZd;(uTL^7$)xiKbzWJ^a@qU~!>M!VA-+tw z;40lzA#gKC;_JuUFsmr#ORobHBh%|Z4vSkrEC=Q!oK98UO_^ocXlCYCv*)gw9aTFn z%4Q23?cVq*8p^KqsGme-{VtoKyUP9o-KI0U-f9;?zGv<_wcL?LK>Dtn* z#(Y)^i|^7os;eh6-(6s5W?WlvskMzWBZKMJE;;)3%^3GOaFlfo%22GScofugj0Jbo z!AoXOO^ug?pGm^*^zm%!;E*peYW`E*E9qhmf9;Zv52lJ|sikMrM`&CQEFRBvzctDK z&*a$dH;QZC+w?}4!2F(3O&8B>f*K#lZhFk&nCa+#YwV_19bFq8qnh(6xJ8zbSst-WqJ`SC)T~n*MbUxFuu9bVvTI$a zP!Sb*1k7C^BV$KG*~s~kB8)cg zNg4yHnw#~`iZZw;#U*iIMSrf0I4X5KXs(RVL1B7ebm}x=5{Z40ND*Qjm~BLc=qzZ3nxy8iQP=Q;f4C>C*&6x8rhW3qqcl(t1ZpgPO;ff$Ovuk|n(W`{K$_Tip zA#;huYZ#@QsF#n z1_l-3Nj3%5BHOX#Ydk}PFM5b-)py|y?dS_~{`C_+Kj39qq z=vM^&D;ximxxB#t{>BFwiE~$GRo4H{_D93r2PZ+}(D0~kqs02H7VRSU+ACxV;~z0D zcCZsME&(#JTx|E<>;kTqn2R9B`|_Mfq|pS2zGSSYh&~|)RM2NMkz`Y4TIFDQ)v*DW zlP6_l(O~v%Lelg_0V7LtigHpbKKlp=xUeVyjBN>Pt=IXN4s z?$fEI$kpCrVQs6pw^$?ul!KD-MVdE@u^a4}IPlJFn^ElSp= z2Sz2!(gPlEXD6AsTKxNxDF_40wiRR25PJC85PFYyzgjFJPs=K|)rhRn%pFs|VF;8* zEnS!cekNcxHOAD1IlwD9gEUacZZ+X()Ta`T2peMK7$q^x5@ZH43Ba=lAZrz?l0H;J z?96e6Hj+(WJQ8JajXM+ap-X>V8?^9F`plM6|Kr}xcgo1>2eaw=0ra!4iE&c48!;Rp zU$8%t_0mL9`thuV;aZS~h1aYrV;d|AtWj4kiu7^-V zfnTj8GQe0mkO>~Nru%oK#t+le3VU*`Q)`~pRI>x+gO%D?+OJ~>(5HejrOg_bE;<=4 z3XioVhZuNK6(_F&0zN|WHMfnhrjhbnVRw$>CQ4%JX3f-P z7ORH(F6o-vA_>mf1a(e%%cn2HM~_dPkzG>8fx3D(?AEO&HNwk@DR2x8v-QXg@#6GxPjZ6h+5g5o2uRJ% zi_~9G1C|SzPm{*@07|c&iK#I)SJz)}BO%}#^*SutuF_7ifWX!Zevtxn4DvqfV z8nzF;L^7dLSpOz0kgT>yI1NjwaF@UX`F%|T5ytLxW;i(|oUBNzlH}+_fo@RjpM*Bx zE@mVDJ4Q3qstA;GbgQLo>7-RS73oBif@>O{s2i5i6>SuP-!LJZ92rh}!`{wK&|hz{ z1W#G(dy7Sdw5|gIBa^{Q_+M<6)_wuunjA0EaXx+IFuj}x1<@->$_w@JF?GNe7{em{ z42i$!RU0wU{V#}HE@2f7J|hIZcN-0*>X?-0OTEL3Fr)~L+-7>3UWX*`Y>ub5fP-7M zPDp|*QH|&J!>d*U5q5h*1OV&%U*m3z+{y`=a+>7 zyIG90s1o#&JU2{uW(pMeAZYlX3CaW&!q=B5RWCz3m^Q2na%}!OY;K*!hib*%LIu#p z*?FLCrCu{Jfp*PtdgsRkkx~wyv_X@X~;T6G>VHSpsIcrhoUy0_X0!u;!!L&i_H=-7eJpF1=#IR zKpZV)5IfYjsI4<45HI7}V$L!by1QS^ZG%QVO=w%8C@k=zG1uL1pQ(`C6fG(ek3M?dTD>H?v!&@7(yBYjvw*&bN+tA-~IeO%wH*gkMp;Kzi0W| z&);kO^~raHCx0-S09^0(&nc4$@-(W4grQhY^BWPEV-pjY*B|v6DZzTjGn5mjoSsSB z1NHqw;!u&?`(DsUVcNCX?CXEkh3&Gkrx2s@m!$=}zg_7al8R{YZEB$>_anGQ5&jTAqI?+N05zKkk~ z52MNlBtj}pVfE^^ljDbr(Og) z9e3dE{4y!o)a~&tbP`kl&CpOYS&^@$DZ86Z8mY92%zILLoB>$6L+R)K(tJOo;_GOm zsqa&?m?#vp%=oNZ%>Tk{>X-3Vx+~f2WP*Q+#IMqyU*a>Hx<9^5e>Hwd zpxPb}>#uqG>z;T(e_f=7-t5G+_?>B3)798)>eum^I(s688F(UvdHJO5`{FZn_OX?A z_E!l=$Y-CLP5mHVsk0BlZQ0b1<7N8mSlc%yZJUCjJt+Py(|)x!2o$Sc|!t0@5g=Fly?Zy7*fZ7b|`#;a8`l z0!R~IdR+T6!pyrEicLM$>^*SH$JDPOHIV>A5ZC#?Ob{C804o%a{&zvjb=HciQwzdFs66T8iq1+YYwl( zts`R!s8y3q6cc)b!{K2Ngq^a)9w@{q-q~4p5lN1uaN_J+N!uHsqtmd1kr5ZT}#}U z`aQ3)nEEGO$xQs0%m^G=`GnMDaV}2zX_UP^uCCAP%HM`XIcx?i99=h($*vOq7A^G% zVy78~Mz4P#StlLm0-zFKL@d=DRiR(xSg0VBSS^z3ynKQ){ZwJ%T1h(nq$HOpI_>1S zwKJzF_OQSYlg|U(N9AxIIPDkEaY|mN(Ze>rC|MB9?kT0#;1A!@YNE{wfUqY8KQ%5xAvqMu8b<<4C#_9evd$s;R|qp{y&KIe)S_16?0#c+Cl}60r#t< zt||02`62-%IWEBoG%bEMYSQ#rry*+Oc3Lg1l zfY-ug@viwrM^?)_H*3iIwE_9xkqI6FFJGYLCgr1RK0bu7U~I$z(JZz=x-64p9BySm zoDYwL1+U5I90VA3-xm9J#btIBWY(EQybMIII}L8g;C>Np&%p4gg`{oc)cv_i$z zLQha3^MDj!MSlP1-p#)msxf>l#{9GNQne$#ozfAzJ%C-mPY~^^If|_FMWf8+mo?Ey zG~_v|JO`7$L+4i_E_PzM>@b0?AP=q%!~6B!*b{-` z2f`Wpu>1FR3DiD|bJqS1w{ry%-dd8H&H7vY-Ki0|Kw@k19M<(8(uS;m-py_FF?4A| zoy{8wUwqcvB3mxtTSPxQk=xao>z`W|HHcp_)$ z`oGQ9k5A`Q`dN!zQ0oAdI=@=O&8hZZJKFV%UZImG+ru$~KPKKZ>669Uqca?2So6)` zJzE{Kze3o;pyryl=mmr3=Vs4KPMw{U`+DxzeH_xcPiUg>S@^=^2X)G1ox(2C`xodf zcq>Ha&?JQ$CWkX3Nf;}m@^&o!sWbi8mr`<` zo8v2?@~UFu@clt|+h|$9+e@}DhC<}1r@h>>S$Mi-Z}lp>H(=v-Ewo0+ni`#*G~(Veb8^5YhHK6Sj{=O z0Zz`O=jD#@KJmm8Ix@d39WKwN!xh=yfbUwM>5&&o(0E zkb$`%V1z3kyn;fV&nAl={5&t5Jx6f;=gliVkmnjki49IfX3ZwxXPBXwx@Dan$WuD-ux zgG*mMKxnrIj|W%Guz5U$3mQX&5j=>KaKPNx{cdsk^-F4w;+RF^aJyFe#F-m<{2Tf= zJhJuM64c<`_87OMrC)VcJ>}i}9m*_7u3O;ke2%w#bVSE%)nRpLatm5!@uop@tTx!5o*I9wKq440AO%&r&s7^_ z?|GCP(!8B#Q``Ci9p?I~PAz4U{HvyWGvg(z&RnYK$?T`d8RF#Ye#_PU&Lotx^lMH8 zne5uZ zguz^T&|K(Bzj{f>@rmBfTO|e8{)vg+Tb^co-p%7=Q94oLz2)nCCMPUflngFPPG98R z41GHmF&>sX&SN@{rrY}6E^lW8E%Z&6#H9zVKhm-H7CDAmo8m==0~CE9NShmufQ`3# z%UoCJUj;O!()V1#D{LHkJNJ_LD?y`o^8kCez8wHL&zSY!g=sfEE=AWcbySctklh}E*eU$Tu9q4iE4Oaym^0$+}`MJL-JYVF`V75;1T*6bYE4CveyJ|*6#dVh+ z@s1zcx1z_EYh@hG(xT1Tm2T^HyHX{lt6Pp}ZI?rVT5jVF7{Dc}?`cRaGgoothZ~2` zUFe%~6QA7nM0DZ=X0u-IYc&uCsRp+EgHiP_*;MU@aY#@1IZ_qoK({LHes|ItLKpsl z4I|`IP}b(;k6B5H{YMTYBMXu>3zFm0eMPCa9{M#aiRIaTk33#^=m$LVJdba%mZ_st zqrlu@bz^fS%h?6|0!Ww7DA<0xRQrXwrW^_zg;NL|l3^9;!ReaYZs290*12d#bu3!^@wI-DFAk!0aWh=<=fklqMBMkb1)>ic*p&=c@;h2;cr zSf2n6>l46UF(PxV$r5hpJt9Zs!uVgyt3vM9+}ama=V0Gw?c>ZLWfKMIF0n7H!~o4? zh_&J(-MXzJr4P>^-dpJS%-r$*nA*04nO(~*SAXIrOqoZ)Pg9N@7Bz8puNynBxyF@l zcNd%cP_oV!jT6;rjVM`1i9Z47s0W!~S47n{is9`~yf;PS_k;VG`V>j&ofXUR+6Zd2 zru$@zI%i`}%Waeujit2Te6csk{Xt0F&J6#vX}ty-Wc}?kCGnB2v|5}@iSKJ=T8&R$ zBks@;QP`6tsaWpsf{YP8+X5v2`dh6VB zpwX(J=t!nrJ&)f?-$ZBBmE!I}d+k*>K?U=+IyWN?^b$C9IG~Yytpe2Suj%Tjy;yWY zZ0;Nr#v9Y!&xHmJZi|?iD^RFMU$dvo*)ZMgMsqhO>xxP?u!)tB4_u}`xHk`26K0tffP9) zEf<`LHHpvyvx#t%*%`UE#T;doaLLITa$U}2tN&Hg6$6GU*c-fY0FEi-&>gswz1kU^ zSIyK9HliyN>yEqup?jrRmoD~gU5wid?0$3I9!J|rA9w0d!?@!eHsi9Jk;ojVO)K!& zIwWFE#&Pwp7?%VmdYY3Gn;7@xE7aH(@*XGFTf%d4SI;2iOphMT)=1%njfJW4k@$Qn zN=F|dVd=;k*BFUc=F;BMX#`g0_A5E<9A6cF-nO9l|7Rpzt^Qt?SgzTfnV1xBqpDSs z7#olC(xm>nUh|nzY_!W9&(kbCMp&4QRbT+cIlar*YFwYcoicrR*)RhNU9qXE|1~2p z>hCjxza)~6Ip(T_RYv{&nfm*qMtD^wxE&w2`q}NAtW5nl-Xi=}oVaK@q*a@S(+GvKW*W0bM2s5aDUO(`qA-PyhGrF}4hZUc^`P`oX-SMFM3D)w51BF;unxUC1{h{;MjE>)qsNyn17Euux;?vfbS1_bqq2dC}K(=zA zZH=Dn1uF0`G|l?3j~ENCx6j}4=9mPFZEqbvhn>gNWYP9({vIF%-j>u2Ify4uY(mv|P98+{!c!;UqlXXc?_5CirCx!Ir_9l(>1D;n5JHB`4tcp8iqgR@$XWm^OO?hPY2HTFlnED7C;N)eil z05Ca2g@{pe(1;{TO32lP>I$kj-CDZR2+b7M(qUrhl}W4`t0dY`Hvu|At5^|Bg|9J# zg;t5G<>KPSTae3wRNt0RaeDnSM{=wjaM=tUfxc)qvP@swT8&_S0?8G7RN(gaa+L#` zp9G{iyTI27psj7MCX}LmbbPm-uXP9nJbay z7R2j;Q^ZWDY!K^*A>vl6Ko5(E^!M1Z^RxC~Y+y>%?eKaY5`?q!X+#K}02Ex@;C4PP zCMMS0uur-qgbHUhb71$xkhDZ$>U=D=>-EVp;*V^mfwJUwIUPNq=`cqfE3>d0Ecf7U zAYAG1UhPeVLZ>niK5fIU4DpXWkF~rBs0u#w~Qwb z0(|h*T$>LI%}Dgf6GJpvdhVWC4aKQ4NF%{Ti2cm$b6Wip+xa|e#+HU+kzeCxEiC3N zngCFu83R&gzraWZ-Eut>jKQ~Fp_=bHcAR!D(Vz`8z}8Ul?1JRv&2rFJUvbE_1&0}- zx=>oQ65cTFA5(O0r;pyC;oJus_mKJDR zT_>r4X5UW|c3UoNXvM=Q>dD1a$;l4HlynPn(PkN5RSS1*3~mk8ZV3_#my~rjSO(K4 z)ka#SCnLxi$mCqT1=~=HuW%;YCw8&i3UQqM%}NBifQ!Ij^#KS?G64^Qe`|0QLSjp( zR&8Rut>|1dOY1o;my33UJ+JQ9);SGXb`1c)v_3!*9tu^kIXWDPPh2H~zYva*pfT)q zc>bd5?U|y?RLVWbwjZe7=isoIoHG$ol|4TjWnU&ya9Sj74Z0a)CddK7h{6pAiRpCt zeTerM;UNRvh7z z6+h%Z*7oKQys`Ltna`OZV+P zBc|n9GEpnQl^*Yk0hJN}nz>G7A#_o*U$X1UOqY{UeTyvlnD2DyZlsC|4kZ#^eFPt= zBv$9T>hWaxU5Q8Jw@5UEm3F?d;)oh8MZl=;ivvgHawU%IR=PQh9Q%^%HkG3^p)DOv zXVz6c8x_ zMGBx z4&P{*ZjS?TyD#~LCN_we7cgh8UMgZU&hZ)gi@0+U=rjvkGX$|hgb96#=t^{G$6*^%SKW@TrAvIBm zv&$ZxR+%eQ9#bdICU%3Mu$X#}Hy!q2QzVL%xcI!==9DBw?SQVWl8mcU2ozgDqm29! zUB#XVcq?kInpLwWh7J5wGg5{HDl^wzyKZpfSpTdIMcH~!a?G4`m&gB98+Wcs4Cpm` zuw#xDE9rZ-E}| zx=JH7-mQ2!*_pH=xS?PLxDBO7>08nd)6MEA=((q+O}2%@Q%AUK3GAHaQ&_@%P&aYa;4( z5@2`RuzySzqm=j|Yqmy9NXu0o@z}3>UC2Nw@i!tWwkEJ82xRBG*}yvZFuAv7R^4pt=b+=^+~;s)6LdllJnm%z3oMgkz8KcKEa$@UR%C}R$N=8 zl{x-}m2Fnd(b~&r;-a4G%ed~VE#t~Kxr;JCzkBpvG~{1Bv+aQNI;;J3>9wr3yqKSX z@>FkKP-MEQJ6;_`xYC`+f5WV{gZX9Aoa;HMsdn8|#|^~UtS-aO zE@&w}sqCe?tlVX<$1u>gp9?NOhSzFcBG=04nboBqt3U4K`Y!)K+p9U=O1DR3?nB{| zI{Sy-C!h$kzqAcoC%B`MMd^d<3%TO6tIXfMrr(TK_@BG}tlSh9o>cwkx;o|#f7HGD z?^cO6Lh<#&nt#bgwV|5J?>8Nn&G5#${KiAfc1T@z@DtLv`T&kV2^>9e$$ z3C%E{?tZn@^uXoqWAfRfbhgA!A_qBZjub<-;9Bx-z5_a3kc@L(^ac8QXRh;a zm>)5pH^P-#12RKMX(yZL>3Gncp_$H5wKFusiJ1_AyO_$9zm~#gc)G#Ms_Dqb6A{&m zp2K!N-BpBzk=kNPb#_bR?p)*JC?Osa@3v4$b6$+|ivqS7my%@>0pj<9DELzZ?+IbE zuE28#*{`=`=Fcfa`1f$5OKp24bhS$>&RAsID;=r#xUNFm1hD8IcWof(RBkv(6$=Sl z|J)i7$;buaC8;+gk|lNUm+s`tG%Xn7wnXn{gdt(ENNsjb<;m#?iAwVX246GkNx$oJ zzQ8%99gCD~6Etu#eK7N@C&_`J`SkRF95uULuxj zW2`0tkq;#~`G8>QGS`#=PUrO$;ztMwv0$s-$JO@>lCu{is~05CSdg5&An93q|q!jI|a$w|cWUi6&U@IvW15ts0Z8@H?Vvbgd7j>5o1Z8_@R5In`BYXSj5aS|322rFI79Zw_y~P)w)$IZ>)B!jIynD zrCS#jRXRIf9T?fYWuAmXfr52<9bJuSYj_cT84HYT*g??NhT1LlS#wE14AeNX%_Z}l zdyFLk&P0FmCk0pujb*;xKv@Y?housGIp=|GSLMU+5TOywvax1CksW%SUZ!lV1k;2P zanC}XURUi%{nOS3>>wm^{~Z!muQmg48;H zySuGVBR-ZGk@=1ssckh28HrbmEisC{hmimq;||x7)IVAY5sI3t(bv&OFdw2UtQsn~ zTNflHy$;Da&J5U+Q`-2Aoy9MUtc&ZcD~4*_rnA%r2DB?Qp`e93UYJjFtt7pbBne%lvWNv$0NHhjc%D+q&QKO^+z#ArVgr& zRC401n4xy^*1<24fN<>%nqSq+>CG|*QfP$2_H8R-k?9wDV~07RW`7mhk3?yNy_

52S<r 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

}#W+yScl~^mFxU@*jMycgQMdb9B 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�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%0AE5 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%0AS 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
~>Mxmc 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 BfsFtF literal 514560 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,jV*)uZk&RJ9MdBa9i6-or}V*qO~P+QuGw>QqRf) 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%0D`kMbsU`+gzZ=vGyUm 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�&r14gN%}@g_?|@iABl9X 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 From b90f767367a6ccb4afaea0e8af924141b3eb3620 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 2 May 2016 02:03:51 +0900 Subject: [PATCH 131/585] Cosmetics. --- loader_example.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/loader_example.cc b/loader_example.cc index c44e0e76..0aec2d1f 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -55,10 +55,12 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(f), static_cast(fnum)); + // For each vertex in the face for (size_t v = 0; v < fnum; v++) { tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; - printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast(f), static_cast(v), idx.vertex_index, idx.normal_index, idx.texcoord_index); + printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast(f), static_cast(v), idx.vertex_index, idx.normal_index, idx.texcoord_index); } printf(" face[%ld].material_id = %d\n", static_cast(f), shapes[i].mesh.material_ids[f]); From e9a7c76c235da94916cdec478552251f8fe8957e Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 11 May 2016 23:38:36 +0900 Subject: [PATCH 132/585] Add build script for Windows. Fix build on Windows. --- examples/viewer/README.md | 17 +++++++++++++- examples/viewer/premake4.lua | 43 ++++++++++++++++++++++++++++++++++++ examples/viewer/viewer.cc | 2 ++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 examples/viewer/premake4.lua diff --git a/examples/viewer/README.md b/examples/viewer/README.md index 4e0e0872..8cb41c17 100644 --- a/examples/viewer/README.md +++ b/examples/viewer/README.md @@ -1 +1,16 @@ -Simple .obj viewer with glew + glfw3 + OpenGL +# Simple .obj viewer with glew + glfw3 + OpenGL + +## Build on Windows. + +### Requirements + +* premake5 +* Visual Studio 2013 +* Windows 64bit + * 32bit may work. + +Put glfw3 and glew library somewhere and replace include and lib path in `premake4.lua` + +Then, + + > premake5.exe vs2013 diff --git a/examples/viewer/premake4.lua b/examples/viewer/premake4.lua new file mode 100644 index 00000000..66da6f9a --- /dev/null +++ b/examples/viewer/premake4.lua @@ -0,0 +1,43 @@ +solution "objview" + -- location ( "build" ) + configurations { "Debug", "Release" } + platforms {"native", "x64", "x32"} + + project "objview" + + kind "ConsoleApp" + language "C++" + files { "viewer.cc", "trackball.cc" } + includedirs { "./" } + includedirs { "../../" } + + configuration { "linux" } + linkoptions { "`pkg-config --libs glfw3`" } + links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" } + + configuration { "windows" } + -- Path to GLFW3 + includedirs { '../../../../local/glfw-3.1.2.bin.WIN64/include' } + libdirs { '../../../../local/glfw-3.1.2.bin.WIN64/lib-vc2013' } + -- Path to GLEW + includedirs { '../../../../local/glew-1.13.0/include' } + libdirs { '../../../../local/glew-1.13.0/lib/Release/x64' } + + links { "glfw3", "glew32", "gdi32", "winmm", "user32", "glu32","opengl32", "kernel32" } + defines { "_CRT_SECURE_NO_WARNINGS" } + + configuration { "macosx" } + includedirs { "/usr/local/include" } + buildoptions { "-Wno-deprecated-declarations" } + libdirs { "/usr/local/lib" } + links { "glfw3", "GLEW" } + linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } + + configuration "Debug" + defines { "DEBUG" } + flags { "Symbols", "ExtraWarnings"} + + configuration "Release" + defines { "NDEBUG" } + flags { "Optimize", "ExtraWarnings"} + diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc index a86c853b..31606432 100644 --- a/examples/viewer/viewer.cc +++ b/examples/viewer/viewer.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -19,6 +20,7 @@ #include +#define TINYOBJLOADER_IMPLEMENTATION #include "../../tiny_obj_loader.h" #include "trackball.h" From 48839e3b07f754cf3c6350746e55d31b900f7219 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Thu, 12 May 2016 01:13:47 +0900 Subject: [PATCH 133/585] Add screenshot of glviewer. --- images/sanmugel.png | Bin 0 -> 192538 bytes tests/README.md | 12 ++++++++++++ 2 files changed, 12 insertions(+) create mode 100644 images/sanmugel.png diff --git a/images/sanmugel.png b/images/sanmugel.png new file mode 100644 index 0000000000000000000000000000000000000000..32ea150f3ec53aa87d2db29b37f3eb4649d6a674 GIT binary patch literal 192538 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Ü}<5eA%8+6G^k5U9v3B9+oDi-RDOSj4De0>SG7bo zWtsM2pLW}()^&lneG-1^n*C5!>FUiaa7;0ENXF<40r!lEr@U)& zp`NhqrI6Y_x0>{jAB(u?=78C=7;nLJb=4gmxE$&lrznNh+1AID$Lc%ut!(>KMUqa% zgdZnY>nd6n_X`dj%o{P>g60Db3``i1&6Q*p(c3Tk!avcI;%0l1!abMs3@3Ij8b!$* zxrEJo+7kv?dAN{3huzr5F5&@B#u`SuNz+WiM9Qpc1C?Vg7r$7eT`Z~RXtan zj4gmdNUTr*>h3GsT2`-KIRAFTZPZHk@qIoa8v~e@bfz4OannvZ6Xbs<=<7LKDoQc8 zB;>*zaOhFS0wwc?&l9Uf7fEsVU3E;FMK~N!c5-(Tq2~D5)~7UbNBJImF*e|WB_ByA zAX^r(1r6H{n&(h{U^Lcs8@&=#W0@58Dd9zv1@OMFO4^hVOE#FuAnRJGyJ#jytv=^+ zM+%AG*Le|Due)x|8~J-E0jj#w4yMb! z#rHqyXdEScy5ucr=Gy)x<5=20_DPP#QU2O%o^ilw+;;W@mWWWybql#%c9L>&6Q(c= zeP1w5v}zyx|X@O%{HP z`0*3W+ck@eUEfZ8eYoDkA6~N)=kv~Wf$mU8>SqNVC1C!P-FA;ah|s-bSYEW1_6Lk3 z+Xmds-n-y|Na`XUe<9(`TgpB|d)Ug-d)vR+#WaEg))JAl0dQxT4vREKW5y`^$s>`x-Np# z>urAzDbmJ(|u1PM2P_%q?cTb>e<%=PfHi0 z9zYEEn^5n-�iO5=}k;C%ehUi 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