{"id":20862438,"url":"https://github.com/nmwsharp/happly","last_synced_at":"2025-10-18T16:01:27.825Z","repository":{"id":42522299,"uuid":"125111670","full_name":"nmwsharp/happly","owner":"nmwsharp","description":"A C++ header-only parser for the PLY file format. Parse .ply happily!","archived":false,"fork":false,"pushed_at":"2024-02-07T16:26:06.000Z","size":716,"stargazers_count":337,"open_issues_count":15,"forks_count":71,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-01T05:11:17.487Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nmwsharp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-13T20:40:01.000Z","updated_at":"2025-03-31T11:23:08.000Z","dependencies_parsed_at":"2024-05-01T12:03:11.409Z","dependency_job_id":null,"html_url":"https://github.com/nmwsharp/happly","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmwsharp%2Fhapply","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmwsharp%2Fhapply/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmwsharp%2Fhapply/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmwsharp%2Fhapply/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nmwsharp","download_url":"https://codeload.github.com/nmwsharp/happly/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253709153,"owners_count":21951110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-18T05:23:55.507Z","updated_at":"2025-10-18T16:01:27.585Z","avatar_url":"https://github.com/nmwsharp.png","language":"C++","readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/nmwsharp/happly/blob/master/happly_logo.jpg\" width=\"200\"\u003e \n\u003c/p\u003e\n\u003cp align=\"center\"\u003eA header-only C++ reader/writer for the PLY file format. Parse .ply happily! \u003cp align=\"center\"\u003e\n\n### Features:\n- Header only-- drop in and use!\n- Read and write to plaintext and binary variants of format with same API!\n- Supports general data in `.ply` files, along with common-case helpers for reading/writing mesh data!\n- Automatic type promotion-- eg, if a file contains a `float` field, you can seamlessly read it as a `double`!\n- Tested, documented, and MIT-licensed!\n\n[![actions status linux](https://github.com/nmwsharp/happly/workflows/linux/badge.svg)](https://github.com/nmwsharp/happly/actions)\n[![actions status macOS](https://github.com/nmwsharp/happly/workflows/macOS/badge.svg)](https://github.com/nmwsharp/happly/actions)\n[![actions status windows](https://github.com/nmwsharp/happly/workflows/windows/badge.svg)](https://github.com/nmwsharp/happly/actions)\n\n## The `.ply` format and hapPLY\n\nThe `.ply` format is a general-purpose flat file format useful for recording numerical data on unstructured domains, which includes both plaintext and binary representations. The format has been kicking around since the 90s: [Paul Bourke's webpage](http://paulbourke.net/dataformats/ply/) serves as both an introduction and the most official specification. [hapPLY](https://github.com/nmwsharp/happly) grew out of my own personal code for `.ply` files-- the format is extremely useful for working with 3D meshes and other geometric data, but no easily accessible C++ implementation was available.\n\nAlthough the `.ply` format is commonly used to store 3D mesh and point cloud data, the format itself technically has nothing to do with meshes or point clouds; it simply specifies a collection **elements**, and data (called **properties**) associated with those elements.  For instance in a mesh, the elements are vertices and faces; vertices then have properties like \"position\" and \"color\", while faces have a property which is a list of vertex indices. hapPLY exposes a general API for reading and writing elements and properties, as well as special-purpose helpers for the common conventions surrounding mesh data.\n\n## Examples\n\nRead basic data\n```cpp\n#include \"happly.h\"\n\n// Construct a data object by reading from file\nhapply::PLYData plyIn(\"my_file.ply\");\n\n// Get data from the object\nstd::vector\u003cfloat\u003e elementA_prop1 = plyIn.getElement(\"elementA\").getProperty\u003cfloat\u003e(\"prop1\");\nstd::vector\u003cdouble\u003e elementA_prop2 = plyIn.getElement(\"elementA\").getProperty\u003cdouble\u003e(\"prop1\");\nstd::vector\u003cstd::vector\u003cdouble\u003e\u003e elementB_listProp = \n    plyIn.getElement(\"elementB\").getListProperty\u003cdouble\u003e(\"listprop1\");\n\n// Type promotion is automatic for numeric types: even if this property was stored as a float, \n// we can access it as a double\nstd::vector\u003cdouble\u003e elementA_prop1_as_double = \n    plyIn.getElement(\"elementA\").getProperty\u003cdouble\u003e(\"prop1\"); \n```\n\nWrite basic data\n```cpp\n#include \"happly.h\"\n\n// Suppose these hold your data\nstd::vector\u003cfloat\u003e elementA_prop1;\nstd::vector\u003cint\u003e elementA_prop2;\nstd::vector\u003cstd::vector\u003cdouble\u003e\u003e elementB_listProp;\n\n// Create an empty object\nhapply::PLYData plyOut;\n\n// Add elements\nplyOut.addElement(\"elementA\", 20);\nplyOut.addElement(\"elementB\", 42);\n\n// Add properties to those elements\nplyOut.getElement(\"elementA\").addProperty\u003cfloat\u003e(\"prop1\", elementA_prop1);\nplyOut.getElement(\"elementA\").addProperty\u003cint\u003e(\"prop2\", elementA_prop2);\nplyOut.getElement(\"elementB\").addListProperty\u003cdouble\u003e(\"listprop1\", elementB_listProp);\n\n// Write the object to file\nplyOut.write(\"my_output_file.ply\", happly::DataFormat::Binary);\n\n```\n\nRead mesh-like data\n```cpp\n#include \"happly.h\"\n\n// Construct the data object by reading from file\nhapply::PLYData plyIn(\"my_mesh_file.ply\");\n\n// Get mesh-style data from the object\nstd::vector\u003cstd::array\u003cdouble, 3\u003e\u003e vPos = plyIn.getVertexPositions();\nstd::vector\u003cstd::vector\u003csize_t\u003e\u003e fInd = plyIn.getFaceIndices\u003csize_t\u003e();\n```\n\nWrite mesh-like data\n```cpp\n#include \"happly.h\"\n\n// Suppose these hold your data\nstd::vector\u003cstd::array\u003cdouble, 3\u003e\u003e meshVertexPositions;\nstd::vector\u003cstd::array\u003cdouble, 3\u003e\u003e meshVertexColors;\nstd::vector\u003cstd::vector\u003csize_t\u003e\u003e meshFaceIndices;\n\n// Create an empty object\nhapply::PLYData plyOut;\n\n// Add mesh data (elements are created automatically)\nplyOut.addVertexPositions(meshVertexPositions);\nplyOut.addVertexColors(meshVertexColors);\nplyOut.addFaceIndices(meshFaceIndices);\n\n\n// Write the object to file\nplyOut.write(\"my_output_mesh_file.ply\", happly::DataFormat::ASCII);\n\n```\n\n\n## API\n\nThis assumes a basic familiarity with the file format; I suggest reading [Paul Bourke's webpage](http://paulbourke.net/dataformats/ply/) if you are new to `.ply`. \n\nAll of the outward-facing functionality of hapPLY is grouped under a single (namespaced) class called `happly::PLYData`, which represents a collection of elements and their properties. `PLYData` objects can be constructed from an existing file `PLYData::PLYData(\"my_input.ply\")`, or you can fill with your own data and then write to file `PLYData::write(\"my_output.ply\", DataFormat::ASCII)`.\n\nGenerally speaking, hapPLY uses C++ exceptions to communicate errors-- most of these methods will throw if something is wrong. hapPLY attempts to provide basic sanity checks and informative errors, but does not guarantee robustness to malformed input.\n\n**Reading and writing objects**:\n\n- `PLYData()` Construct an empty PLYData containing no elements or properties.\n\n- `PLYData(std::string filename, bool verbose = false)` Construct a new PLYData object from a file, automatically detecting whether the file is plaintext or binary. If `verbose=true`, useful information about the file will be printed to `stdout`.\n\n- `PLYData(std::istream\u0026 inStream, bool verbose = false)` Like the previous constructor, but reads from an`istream`.\n\n- `PLYData::validate()` Perform some basic sanity checks on the object, throwing if any fail. Called internally before writing.\n\n- `PLYData::write(std::string filename, DataFormat format = DataFormat::ASCII)` Write the object to file. Specifying `DataFormat::ASCII`, `DataFormat::Binary`, or `DataFormat::BinaryBigEndian` controls the kind of output file.\n\n- `PLYData::write(std::ostream\u0026 outStream, DataFormat format = DataFormat::ASCII)` Like the previous method, but writes to an`ostream`.\n\n**Accessing and adding data to an object**:\n\n- `void addElement(std::string name, size_t count)` Add a new element type to the object, with the given name and number of elements.\n\n- `Element\u0026 getElement(std::string target)` Get a reference to an element type contained in the object.\n  \n- `bool hasElement(std::string target)` Check if an element type is contained in the object.\n\n- `std::vector\u003cstd::string\u003e getElementNames()` List of all element names.\n\n- `std::vector\u003cT\u003e Element::getProperty(std::string propertyName)` Get a vector of property data for an element. Will automatically promote types if possible, eg `getProperty\u003cint\u003e(\"my_prop\")` will succeed even if the object contains \"my_prop\" with type `short`.\n\n- `std::vector\u003cstd::vector\u003cT\u003e\u003e Element::getListProperty(std::string propertyName)` Get a vector of list property data for an element. Supports type promotion just like `getProperty()`.\n\n- `void Element::addProperty(std::string propertyName, std::vector\u003cT\u003e\u0026 data)` Add a new property to an element type. `data` must be the same length as the number of elements of that type.\n  \n- `void addListProperty(std::string propertyName, std::vector\u003cstd::vector\u003cT\u003e\u003e\u0026 data)` Add a new list property to an element type. `data` must be the same length as the number of elements of that type.\n\n**Misc object options**:\n\n- `std::vector\u003cstd::string\u003e PLYData::comments` Comments included in the .ply file, one string per line. These are populated after reading and written when writing.\n\n- `std::vector\u003cstd::string\u003e PLYData::objInfoComments` Lines prefaced with `obj_info` included in the .ply file, which are effectively a different kind of comment, one string per line. These seem to be an ad-hoc extension to .ply, but they are pretty common, so we support them.\n\n**Common-case helpers for mesh data**:\n\n- `std::vector\u003cstd::array\u003cdouble, 3\u003e\u003e getVertexPositions(std::string vertexElementName = \"vertex\")` Returns x,y,z vertex positions from an object. `vertexElementName` specifies the name of the element type holding vertices, which is conventionally \"vertex\".\n  \n- `void addVertexPositions(std::vector\u003cstd::array\u003cdouble, 3\u003e\u003e\u0026 vertexPositions)` Adds x,y,z vertex positions to an object, under the element name \"vertex\".\n\n- `std::vector\u003cstd::array\u003cunsigned char, 3\u003e\u003e getVertexColors(std::string vertexElementName = \"vertex\")` Returns r,g,b vertex colors from an object. `vertexElementName` specifies the name of the element type holding vertices, which is conventionally \"vertex\".\n\n- `void addVertexColors(std::vector\u003cstd::array\u003cunsigned char, 3\u003e\u003e\u0026 vertexColors)` Adds r,g,b vertex colors positions to an object, under the element name \"vertex\".\n\n- `void addVertexColors(std::vector\u003cstd::array\u003cdouble, 3\u003e\u003e\u0026 vertexColors)` Adds r,g,b vertex colors positions to an object, under the element name \"vertex\". Assumes input is in [0.0,1.0], and internally converts to 0-255 char values\n\n- `std::vector\u003cstd::vector\u003cT\u003e\u003e getFaceIndices()` Returns indices in to a vertex list for each face. Usually 0-indexed, but there are no formal rules in the format. Supports type promotion as in `getProperty()`, and furthermore converts signed to unsigned and vice-versa, though the conversion is performed naively.\n\n- `void addFaceIndices(std::vector\u003cstd::vector\u003cT\u003e\u003e\u0026 indices)` Adds vertex indices for faces to an object, under the element name \"face\" with the property name \"vertex_indices\". Automatically converts to a 32-bit integer type with the same signedness as the input type, and throws if the data cannot be converted to that type.\n\n\n## Known issues:\n- Writing floating-point values of `inf` or `nan` in ASCII mode is not supported, because the .ply format does not specify how they should be written (C++'s ofstream and ifstream don't even treat them consistently). These values work just fine in binary mode.\n- Currently hapPLY does not allow the user to specify a type for the variable which indicates how many elements are in a list; it always uses `uchar` (and throws and error if the data does not fit in a uchar). Note that at least for mesh-like data, popular software only accepts `uchar`.\n- Almost all modern computers are little-endian. If you happen to have a big-endian platform, be aware that the codebase has not been tested in a big-endian environment, and might have bugs related to binary reading/writing there. Note that the _platform_ endianness is distinct from the _file_ endianness---reading/writing either big- or little-endian files certainly works just fine as long as you're running the code on a little-endian computer (as you problably are).\n\n\n## Current TODOs:\n- Add more common-case helpers for meshes (texture coordinates, etc)\n- Add common-case helpers for point clouds\n- Bindings for Python, Matlab?\n\n---\nBy [Nicholas Sharp](http://www.nmwsharp.com). Credit to [Keenan Crane](http://www.keenan.is/here) for early feedback and the logo!\n\nDevelopment of this software was funded in part by NSF Award 1717320, an NSF graduate research fellowship, and gifts from Adobe Research and Autodesk, Inc.\n","funding_links":[],"categories":["Miscellaneous"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmwsharp%2Fhapply","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnmwsharp%2Fhapply","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmwsharp%2Fhapply/lists"}