//////////////////////////////////////////////////////////////////// // PLY.cpp // // Copyright 2023 cDc@seacave // Distributed under the Boost Software License, Version 1.0 // (See http://www.boost.org/LICENSE_1_0.txt) #include "Common.h" #include "PLY.h" using namespace SEACAVE; // D E F I N E S /////////////////////////////////////////////////// #define NO_OTHER_PROPS -1 #define DONT_STORE_PROP 0 #define STORE_PROP 1 #define OTHER_PROP 0 #define NAMED_PROP 1 #define abort_ply(...) { VERBOSE(__VA_ARGS__); exit(-1); } // S T R U C T S /////////////////////////////////////////////////// const char* const PLY::type_names[] = { "invalid", "int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64", }; const char* const PLY::old_type_names[] = { "invalid", "char", "short", "int", "uchar", "ushort", "uint", "float", "double", }; const int PLY::ply_type_size[] = { 0, 1, 2, 4, 1, 2, 4, 4, 8 }; const PLY::RuleName PLY::rule_name_list[] = { {AVERAGE_RULE, "avg"}, {RANDOM_RULE, "rnd"}, {MINIMUM_RULE, "max"}, {MAXIMUM_RULE, "min"}, {MAJORITY_RULE, "major"}, {SAME_RULE, "same"}, {-1, "end_marker"} }; /******************/ /* Construction */ /******************/ /****************************************************************************** Init PLY data as empty. Entry: Exit: ******************************************************************************/ PLY::PLY() : which_elem(NULL), other_elems(NULL), current_rules(NULL), rule_list(NULL), istream(NULL), mfp(NULL), write_type_names(type_names) { } PLY::~PLY() { release(); } /****************************************************************************** Free the memory used by a PLY file. ******************************************************************************/ void PLY::release() { if (mfp) { delete mfp; mfp = NULL; ostream = NULL; } else if (istream) { if (static_cast(istream)->getInputStream(ISTREAM::LAYER_ID_IN)) { IOSTREAM* const iostream(static_cast(istream)->getIOStream(ISTREAM::LAYER_ID_IN, OSTREAM::LAYER_ID_OUT)); if (iostream) delete iostream; else delete istream; } else { ASSERT(static_cast(ostream)->getOutputStream(OSTREAM::LAYER_ID_OUT)); IOSTREAM* const iostream(static_cast(ostream)->getIOStream(ISTREAM::LAYER_ID_IN, OSTREAM::LAYER_ID_OUT)); if (iostream) delete iostream; else delete ostream; } istream = NULL; } if (!elems.empty()) { for (size_t i=0; iprops.empty()) { for (size_t j=0; jprops.size(); ++j) delete elem->props[j]; elem->props.clear(); elem->store_prop.clear(); } delete elem; } elems.clear(); } if (other_elems) { for (size_t i=0; iother_list.size(); ++i) { OtherElem& elem = other_elems->other_list[i]; delete[] elem.other_data; delete elem.other_props; } delete other_elems; other_elems = NULL; } comments.clear(); obj_info.clear(); vals.clear(); } /*************/ /* Writing */ /*************/ /****************************************************************************** Given a file pointer, get ready to write PLY data to the file. Entry: fp - the given file pointer nelems - number of elements in object elem_names - list of element names file_type - file type, either ascii or binary memBufferSize - memory file initial size (useful if the ply size is unknown) Exit: returns a pointer to a PlyFile, used to refer to this file, or NULL if error ******************************************************************************/ bool PLY::write(LPCSTR _filename, int nelems, LPCSTR* elem_names, int _file_type, size_t memBufferSize) { filename = _filename; if (memBufferSize == 0) { // create output file now File* const pf(new File(filename.c_str(), File::WRITE, File::CREATE | File::TRUNCATE)); if (!pf->isOpen()) return false; return write(new BufferedOutputStream(pf, 64*1024), nelems, elem_names, _file_type, memBufferSize); } return write((OSTREAM*)NULL, nelems, elem_names, _file_type, memBufferSize); } bool PLY::write(OSTREAM* fp, int nelems, LPCSTR* elem_names, int _file_type, size_t memBufferSize) { // create a record for this object file_type = _file_type; version = 1.0; other_elems = NULL; if (memBufferSize > 0) { // write ply into a memory buffer, and save it to disk at the end; // useful in case the number of ply elements is not know from the start // in order to avoid an additional file copy operation ASSERT(fp == NULL && !filename.empty()); mfp = new MemFile(memBufferSize); ostream = mfp; } else { // directly write ply data to disk; // in case the number of ply elements is unknown from the start, // they are written into a temporary file and the header is written // at the end using a file copy operation ASSERT(fp != NULL); ostream = fp; mfp = NULL; } // tuck aside the names of the elements elems.resize(nelems); for (int i = 0; i < nelems; ++i) { PlyElement* elem = new PlyElement; elem->name = elem_names[i]; elem->num = 0; elems[i] = elem; } return true; } /****************************************************************************** Describe an element, including its properties and how many will be written to the file. Entry: elem_name - name of element that information is being specified about nelems - number of elements of this type to be written nprops - number of properties contained in the element prop_list - list of properties ******************************************************************************/ void PLY::element_layout( const char* elem_name, int nelems, int nprops, PlyProperty* prop_list ) { // look for appropriate element PlyElement *elem = find_element(elem_name); if (elem == NULL) abort_ply("error: element_layout: can't find element '%s'", elem_name); elem->num = nelems; // copy the list of properties elem->props.resize(nprops); elem->store_prop.resize(nprops); for (int i = 0; i < nprops; ++i) { PlyProperty *prop = new PlyProperty; elem->props[i] = prop; elem->store_prop[i] = NAMED_PROP; copy_property(*prop, prop_list[i]); } } /****************************************************************************** Describe a property of an element. Entry: elem_name - name of element that information is being specified about prop - the new property ******************************************************************************/ void PLY::describe_property(const char* elem_name, const PlyProperty& prop) { // look for appropriate element put_element_setup(elem_name); // describe property describe_property(prop); } void PLY::describe_property(const char* elem_name, int nprops, const PlyProperty* props) { // look for appropriate element put_element_setup(elem_name); // describe properties for (int i=0; inum = nelems; } /****************************************************************************** Signal that we've described everything a PLY file's header and that the header should be written to the file. This function can be also called at the end, in which case the file is close, the header is written in a new file and the already written content is copied from the temporary file. ******************************************************************************/ bool PLY::header_complete() { std::string filenameTmp; if (mfp != NULL) { // ply data was written into a memory buffer, // now write the header to disk and append the data in memory ostream = NULL; } else if (!filename.empty() && ostream->getPos() > 0) { // close this file, rename it, and open a new file to write the header delete ostream; ostream = NULL; filenameTmp = filename+".tmp"; if (!File::renameFile(filename.c_str(), filenameTmp.c_str())) return false; } if (ostream == NULL) { File* const pf(new File(filename.c_str(), File::WRITE, File::CREATE | File::TRUNCATE)); if (!pf->isOpen()) return false; ostream = new BufferedOutputStream(pf, 64*1024); } // write header ostream->print("ply\n"); switch (file_type) { case ASCII: ostream->print("format ascii 1.0\n"); break; case BINARY_BE: ostream->print("format binary_big_endian 1.0\n"); break; case BINARY_LE: ostream->print("format binary_little_endian 1.0\n"); break; default: abort_ply("error: ply_header_complete: bad file type = %d\n", file_type); } // write out the comments for (size_t i = 0; i < comments.size(); ++i) ostream->print("comment %s\n", comments[i].c_str()); // write out object information for (size_t i = 0; i < obj_info.size(); ++i) ostream->print("obj_info %s\n", obj_info[i].c_str()); // write out information about each element for (size_t i = 0; i < elems.size(); ++i) { PlyElement *elem = elems[i]; ASSERT(elem->num > 0); ostream->print("element %s %d\n", elem->name.c_str(), elem->num); // write out each property for (size_t j = 0; j < elem->props.size(); ++j) { PlyProperty *prop = elem->props[j]; if (prop->is_list == LIST) { ostream->print("property list "); write_scalar_type(prop->count_external); ostream->print(" "); write_scalar_type(prop->external_type); ostream->print(" %s\n", prop->name.c_str()); } else if (prop->is_list == STRING) { ostream->print("property string"); ostream->print(" %s\n", prop->name.c_str()); } else { ostream->print("property "); write_scalar_type(prop->external_type); ostream->print(" %s\n", prop->name.c_str()); } } } ostream->print("end_header\n"); if (mfp != NULL) { // now write the ply data from memory to disk ostream->write(mfp->getBuffer(), mfp->getSize()); delete mfp; mfp = NULL; } else if (!filenameTmp.empty()) { // append the body of the ply from the temp file, and delete it File ftmp(filenameTmp.c_str(), File::READ, File::OPEN); if (!ftmp.isOpen()) return false; Unsigned8Arr buffer(256 * 1024); size_t len; while ((len = ftmp.read(buffer.data(), buffer.size())) > 0) ostream->write(buffer.data(), len); ftmp.close(); File::deleteFile(filenameTmp.c_str()); } else { // element writing is starting next, reset counters for (size_t i = 0; i < elems.size(); ++i) elems[i]->num = 0; } return true; } /****************************************************************************** Specify which elements are going to be written. This should be called before a call to the routine ply_put_element(). Entry: elem_name - name of element we're talking about ******************************************************************************/ void PLY::put_element_setup(const char* elem_name) { PlyElement *elem = find_element(elem_name); if (elem == NULL) abort_ply("error: put_element_setup: can't find element '%s'", elem_name); which_elem = elem; } /****************************************************************************** Write an element to the file. This routine assumes that we're writing the type of element specified in the last call to the routine put_element_setup(). Entry: elem_ptr - pointer to the element ******************************************************************************/ void PLY::put_element(const void* elem_ptr) { char *item; char *elem_data; char **item_ptr; ValueType val; char **other_ptr; PlyElement *elem = which_elem; elem_data = (char*)elem_ptr; other_ptr = (char**)(elem_data + elem->other_offset); // write out either to an ascii or binary file if (file_type == ASCII) { // write an ascii file // write out each property of the element for (size_t j = 0; j < elem->props.size(); ++j) { PlyProperty *prop = elem->props[j]; if (elem->store_prop[j] == OTHER_PROP) elem_data = *other_ptr; else elem_data = (char*)elem_ptr; switch (prop->is_list) { case SCALAR: { // scalar item = elem_data + prop->offset; get_stored_item((void*)item, prop->internal_type, val); write_ascii_item(val, prop->internal_type, prop->external_type); break; } case LIST: { // list item = elem_data + prop->count_offset; get_stored_item((void*)item, prop->count_internal, val); write_ascii_item(val, prop->count_internal, prop->count_external); const int list_count(ValueType2Type(val, prop->count_external)); item_ptr = (char**)(elem_data + prop->offset); item = item_ptr[0]; const int item_size = ply_type_size[prop->internal_type]; for (int k = 0; k < list_count; k++) { get_stored_item((void*)item, prop->internal_type, val); write_ascii_item(val, prop->internal_type, prop->external_type); item += item_size; } break; } case STRING: { // string item = elem_data + prop->offset; char** str = (char**)item; ostream->print("\"%s\"", *str); break; } default: abort_ply("error: put_element: bad type = %d", prop->is_list); } } ostream->print("\n"); } else { // write a binary file // write out each property of the element for (size_t j = 0; j < elem->props.size(); ++j) { PlyProperty *prop = elem->props[j]; if (elem->store_prop[j] == OTHER_PROP) elem_data = *other_ptr; else elem_data = (char*)elem_ptr; switch (prop->is_list) { case SCALAR: { // scalar item = elem_data + prop->offset; get_stored_item((void*)item, prop->internal_type, val); write_binary_item(val, prop->internal_type, prop->external_type); break; } case LIST: { // list item = elem_data + prop->count_offset; int item_size = ply_type_size[prop->count_internal]; get_stored_item((void*)item, prop->count_internal, val); write_binary_item(val, prop->count_internal, prop->count_external); const int list_count(ValueType2Type(val, prop->count_external)); item_ptr = (char**)(elem_data + prop->offset); item = item_ptr[0]; item_size = ply_type_size[prop->internal_type]; for (int k = 0; k < list_count; k++) { get_stored_item((void*)item, prop->internal_type, val); write_binary_item(val, prop->internal_type, prop->external_type); item += item_size; } break; } case STRING: { // string item = elem_data + prop->offset; char** str = (char**)item; // write the length const int len = (int)_tcslen(*str) + 1; ostream->write(&len, sizeof(int)); // write the string, including the null character ostream->write(*str, len); break; } default: abort_ply("error: put_element: bad type = %d", prop->is_list); } } } // count element items elem->num++; } /*************/ /* Reading */ /*************/ /****************************************************************************** Given a file pointer, get ready to read PLY data from the file. Entry: fp - the given file pointer Exit: nelems - number of elements in object elem_names - list of element names returns a pointer to a PlyFile, used to refer to this file, or NULL if error ******************************************************************************/ bool PLY::read(LPCSTR _filename) { filename = _filename; File* const pf(new File(_filename, File::READ, File::OPEN)); if (!pf->isOpen()) return false; return read(new BufferedInputStream(pf, 64*1024)); } bool PLY::read(ISTREAM* fp) { // create record for this object ASSERT(elems.empty()); other_elems = NULL; rule_list = NULL; istream = fp; // read and parse the file's header int nwords; char *orig_line; STRISTREAM sfp(istream); char **words = get_words(sfp, &nwords, &orig_line); if (words == NULL) return false; if (!equal_strings(words[0], "ply")) { free(words); return false; } free(words); // parse words while ((words = get_words(sfp, &nwords, &orig_line)) != NULL) { if (equal_strings(words[0], "format")) { if (nwords != 3) return false; if (equal_strings(words[1], "ascii")) file_type = ASCII; else if (equal_strings(words[1], "binary_big_endian")) file_type = BINARY_BE; else if (equal_strings(words[1], "binary_little_endian")) file_type = BINARY_LE; else return false; version = (float)atof(words[2]); } else if (equal_strings(words[0], "element")) add_element((const char**)words, nwords); else if (equal_strings(words[0], "property")) add_property((const char**)words, nwords); else if (equal_strings(words[0], "comment")) add_comment(orig_line); else if (equal_strings(words[0], "obj_info")) add_obj_info(orig_line); else if (equal_strings(words[0], "end_header")) { free(words); break; } free(words); } sfp.emptyBuffer(); // create tags for each property of each element, to be used // later to say whether or not to store each property for the user for (size_t i = 0; i < elems.size(); ++i) { PlyElement *elem = elems[i]; elem->store_prop.resize(elem->props.size()); for (size_t j = 0; j < elem->props.size(); ++j) elem->store_prop[j] = DONT_STORE_PROP; elem->other_offset = NO_OTHER_PROPS; // no "other" props by default } return true; } /****************************************************************************** Get information about a particular element. Entry: elem_name - name of element to get information about Exit: props - the list of properties returned returns number of elements of this type in the file ******************************************************************************/ int PLY::get_element_description(const char* elem_name, std::vector& prop_list) const { // find information about the element PlyElement *elem = find_element(elem_name); if (elem == NULL) return 0; // make a copy of the element's property list prop_list.resize(elem->props.size()); for (size_t i = 0; i < elem->props.size(); ++i) { PlyProperty *prop = new PlyProperty; copy_property(*prop, *elem->props[i]); prop_list[i] = prop; } return elem->num; } /****************************************************************************** Specify which properties of an element are to be returned. This should be called before a call to the routine get_element(). Entry: elem_name - which element we're talking about nprops - number of properties prop_list - list of properties ******************************************************************************/ void PLY::get_element_setup( const char* elem_name, int nprops, PlyProperty* prop_list ) { // find information about the element PlyElement *elem = find_element(elem_name); which_elem = elem; // deposit the property information into the element's description for (int i = 0; i < nprops; ++i) { // look for actual property int index = find_property(elem, prop_list[i].name.c_str()); if (index == -1) { DEBUG("warning: Can't find property '%s' in element '%s'", prop_list[i].name.c_str(), elem_name); continue; } // store its description PlyProperty *prop = elem->props[index]; prop->internal_type = prop_list[i].internal_type; prop->offset = prop_list[i].offset; prop->count_internal = prop_list[i].count_internal; prop->count_offset = prop_list[i].count_offset; // specify that the user wants this property elem->store_prop[index] = STORE_PROP; } } /****************************************************************************** Specify a property of an element that is to be returned. This should be called (usually multiple times) before a call to the routine get_element(). This routine should be used in preference to the less flexible old routine called ply_get_element_setup(). Entry: elem_name - which element we're talking about prop - property to add to those that will be returned ******************************************************************************/ void PLY::get_property(const char* elem_name, PlyProperty* prop) { // find information about the element PlyElement *elem = find_element(elem_name); which_elem = elem; // deposit the property information into the element's description int index = find_property(elem, prop->name.c_str()); if (index == -1) { DEBUG("warning: Can't find property '%s' in element '%s'", prop->name.c_str(), elem_name); return; } PlyProperty *prop_ptr = elem->props[index]; prop_ptr->internal_type = prop->internal_type; prop_ptr->offset = prop->offset; prop_ptr->count_internal = prop->count_internal; prop_ptr->count_offset = prop->count_offset; // specify that the user wants this property elem->store_prop[index] = STORE_PROP; } /****************************************************************************** Read one element from the file. This routine assumes that we're reading the type of element specified in the last call to the routine ply_get_element_setup(). Entry: elem_ptr - pointer to location where the element information should be put ******************************************************************************/ void PLY::get_element(void* elem_ptr) { if (file_type == ASCII) ascii_get_element((uint8_t*)elem_ptr); else binary_get_element((uint8_t*)elem_ptr); } /****************************************************************************** Extract the comments from the header information of a PLY file. Entry: Exit: returns the list of comments ******************************************************************************/ std::vector& PLY::get_comments() { return comments; } /****************************************************************************** Extract the object information (arbitrary text) from the header information of a PLY file. Entry: Exit: returns the list of object info lines ******************************************************************************/ std::vector& PLY::get_obj_info() { return obj_info; } /****************************************************************************** Make ready for "other" properties of an element-- those properties that the user has not explicitly asked for, but that are to be stashed away in a special structure to be carried along with the element's other information. Entry: elem - element for which we want to save away other properties ******************************************************************************/ void PLY::setup_other_props(PlyElement* elem) { int size = 0; // Examine each property in decreasing order of size. // We do this so that all data types will be aligned by // word, half-word, or whatever within the structure. for (int type_size = 8; type_size > 0; type_size /= 2) { // add up the space taken by each property, and save this information // away in the property descriptor for (size_t i = 0; i < elem->props.size(); ++i) { // don't bother with properties we've been asked to store explicitly if (elem->store_prop[i]) continue; PlyProperty *prop = elem->props[i]; // internal types will be same as external prop->internal_type = prop->external_type; prop->count_internal = prop->count_external; // list case if (prop->is_list == LIST) { // pointer to list if (type_size == sizeof(void *)) { prop->offset = size; size += sizeof(void *); // always use size of a pointer here } // count of number of list elements if (type_size == ply_type_size[prop->count_external]) { prop->count_offset = size; size += ply_type_size[prop->count_external]; } } // string else if (prop->is_list == STRING) { // pointer to string if (type_size == sizeof(char*)) { prop->offset = size; size += sizeof(char*); } } // scalar else if (type_size == ply_type_size[prop->external_type]) { prop->offset = size; size += ply_type_size[prop->external_type]; } } } // save the size for the other_props structure elem->other_size = size; } /****************************************************************************** Specify that we want the "other" properties of an element to be tucked away within the user's structure. Entry: elem - the element that we want to store other_props in offset - offset to where other_props will be stored inside user's structure Exit: returns pointer to structure containing description of other_props ******************************************************************************/ PLY::PlyOtherProp* PLY::get_other_properties(PlyElement* elem, int offset) { // remember that this is the "current" element which_elem = elem; // save the offset to where to store the other_props elem->other_offset = offset; // place the appropriate pointers, etc. in the element's property list setup_other_props(elem); // create structure for describing other_props PlyOtherProp *other = new PlyOtherProp; other->name = elem->name; #if 0 if (elem->other_offset == NO_OTHER_PROPS) { other->size = 0; other->props = NULL; other->nprops = 0; return other; } #endif other->size = elem->other_size; other->props.reserve(elem->props.size()); // save descriptions of each "other" property for (size_t i = 0; i < elem->props.size(); ++i) { if (elem->store_prop[i]) continue; PlyProperty *prop = new PlyProperty; copy_property(*prop, *elem->props[i]); other->props.push_back(prop); } // set other_offset pointer appropriately if there are NO other properties if (other->props.empty()) elem->other_offset = NO_OTHER_PROPS; // return structure return other; } /****************************************************************************** Specify that we want the "other" properties of an element to be tucked away within the user's structure. The user needn't be concerned for how these properties are stored. Entry: elem_name - name of element that we want to store other_props in offset - offset to where other_props will be stored inside user's structure Exit: returns pointer to structure containing description of other_props ******************************************************************************/ PLY::PlyOtherProp* PLY::get_other_properties(const char* elem_name, int offset) { // find information about the element PlyElement *elem = find_element(elem_name); if (elem == NULL) { DEBUG("warning: get_other_properties: Can't find element '%s'", elem_name); return NULL; } PlyOtherProp *other = get_other_properties(elem, offset); return other; } /*************************/ /* Other Element Stuff */ /*************************/ /****************************************************************************** Grab all the data for the current element that a user does not want to explicitly read in. Stores this in the PLY object's data structure. Entry: Exit: returns pointer to ALL the "other" element data for this PLY file ******************************************************************************/ PLY::PlyOtherElems* PLY::get_other_element() { PlyElement *elem = which_elem; // create room for the new "other" element, initializing the // other data structure if necessary OtherElem other; // count of element instances in file other.elem_count = elem->num; // save name of element other.elem_name = elem->name; // create a list to hold all the current elements other.other_data = new OtherData*[other.elem_count]; // set up for getting elements other.other_props = get_other_properties(elem->name.c_str(), offsetof(OtherData,other_props)); // grab all these elements for (int i = 0; i < other.elem_count; ++i) { // grab and element from the file other.other_data[i] = new OtherData; get_element((uint8_t*)other.other_data[i]); } // return pointer to the other elements data if (other_elems == NULL) other_elems = new PlyOtherElems; other_elems->other_list.push_back(other); return other_elems; } /****************************************************************************** Write out the "other" elements specified for this PLY file. Entry: ******************************************************************************/ void PLY::put_other_elements() { // make sure we have other elements to write if (other_elems == NULL) return; // write out the data for each "other" element for (size_t i = 0; i < other_elems->other_list.size(); ++i) { OtherElem *other = &(other_elems->other_list[i]); put_element_setup(other->elem_name.c_str()); // write out each instance of the current element for (int j = 0; j < other->elem_count; ++j) put_element(other->other_data[j]); } } /*******************/ /* Miscellaneous */ /*******************/ /****************************************************************************** Use old PLY type names during writing for backward compatibility. ******************************************************************************/ void PLY::set_legacy_type_names() { write_type_names = old_type_names; } /****************************************************************************** Get version number and file type of a PlyFile. Entry: Exit: version - version of the file file_type - PLY_ASCII, PLY_BINARY_BE, or PLY_BINARY_LE ******************************************************************************/ void PLY::get_info(float* _version, int* _file_type) { *_version = version; *_file_type = file_type; } /****************************************************************************** Find an element from the element list of a given PLY object. Entry: element - name of element we're looking for Exit: returns the element, or NULL if not found ******************************************************************************/ PLY::PlyElement* PLY::find_element(const char* element) const { for (size_t i=0; iname.c_str())) return elems[i]; return NULL; } /****************************************************************************** Find a property in the list of properties of a given element. Entry: elem - pointer to element in which we want to find the property prop_name - name of property to find Exit: returns the index to position in list ******************************************************************************/ int PLY::find_property(PlyElement* elem, const char* prop_name) const { for (size_t i=0; iprops.size(); ++i) if (equal_strings(prop_name, elem->props[i]->name.c_str())) return (int)i; return -1; } /****************************************************************************** Read an element from an ascii file. Entry: elem_ptr - pointer to element ******************************************************************************/ void PLY::ascii_get_element(uint8_t* elem_ptr) { char *elem_data, *item; char *item_ptr; ValueType val; char *orig_line; char *other_data(NULL); int other_flag(0); // the kind of element we're reading currently PlyElement *elem = which_elem; // do we need to setup for other_props? if (elem->other_offset != NO_OTHER_PROPS) { other_flag = 1; // make room for other_props other_data = new char[elem->other_size]; // store pointer in user's structure to the other_props *((char**)(elem_ptr + elem->other_offset)) = other_data; } // read in the element int nwords; char **words; { STRISTREAM sfp(istream); words = get_words(sfp, &nwords, &orig_line); if (words == NULL) abort_ply("error: get_element: unexpected end of file"); sfp.emptyBuffer(); } int which_word = 0; for (size_t j = 0; j < elem->props.size(); ++j) { PlyProperty *prop = elem->props[j]; const int store_it(elem->store_prop[j] | other_flag); // store either in the user's structure or in other_props if (elem->store_prop[j]) elem_data = (char*)elem_ptr; else elem_data = other_data; if (prop->is_list == LIST) { // a list // get and store the number of items in the list get_ascii_item(words[which_word++], prop->count_external, val); if (store_it) { item = elem_data + prop->count_offset; store_item(item, prop->count_internal, val, prop->count_external); } // allocate space for an array of items and store a ptr to the array const int list_count(ValueType2Type(val, prop->count_external)); char** store_array = (char**)(elem_data + prop->offset); if (list_count == 0) { if (store_it) *store_array = NULL; } else { const int item_size(ply_type_size[prop->internal_type]); if (store_it) { item_ptr = new char[item_size * list_count]; item = item_ptr; *store_array = item_ptr; } // read items and store them into the array for (int k = 0; k < list_count; k++) { get_ascii_item(words[which_word++], prop->external_type, val); if (store_it) { store_item(item, prop->internal_type, val, prop->external_type); item += item_size; } } } } else if (prop->is_list == STRING) { // a string if (store_it) { item = elem_data + prop->offset; *((char**)item) = strdup(words[which_word++]); } else { which_word++; } } else { // a scalar get_ascii_item(words[which_word++], prop->external_type, val); if (store_it) { item = elem_data + prop->offset; store_item(item, prop->internal_type, val, prop->external_type); } } } free(words); } /****************************************************************************** Read an element from a binary file. Entry: elem_ptr - pointer to an element ******************************************************************************/ void PLY::binary_get_element(uint8_t* elem_ptr) { char *elem_data; char *item; char *item_ptr; ValueType val; char *other_data(NULL); int other_flag(0); // the kind of element we're reading currently PlyElement* elem = which_elem; // do we need to setup for other_props? if (elem->other_offset != NO_OTHER_PROPS) { other_flag = 1; // make room for other_props other_data = new char[elem->other_size]; // store pointer in user's structure to the other_props *((char**)(elem_ptr + elem->other_offset)) = other_data; } // read in a number of elements for (size_t j = 0; j < elem->props.size(); ++j) { PlyProperty *prop = elem->props[j]; const int store_it(elem->store_prop[j] | other_flag); // store either in the user's structure or in other_props if (elem->store_prop[j]) elem_data = (char*)elem_ptr; else elem_data = other_data; if (prop->is_list == LIST) { // list // get and store the number of items in the list get_binary_item(prop->count_external, val); if (store_it) { item = elem_data + prop->count_offset; store_item(item, prop->count_internal, val, prop->count_external); } // allocate space for an array of items and store a ptr to the array const int list_count(ValueType2Type(val, prop->count_external)); const int item_size(ply_type_size[prop->internal_type]); char** store_array = (char**)(elem_data + prop->offset); if (list_count == 0) { if (store_it) *store_array = NULL; } else { if (store_it) { item_ptr = new char[item_size * list_count]; item = item_ptr; *store_array = item_ptr; } // read items and store them into the array for (int k = 0; k < list_count; k++) { get_binary_item(prop->external_type, val); if (store_it) { store_item(item, prop->internal_type, val, prop->external_type); item += item_size; } } } } else if (prop->is_list == STRING) { // string int len; istream->read(&len, sizeof(int)); char *str = new char[len]; istream->read(str, len); if (store_it) { item = elem_data + prop->offset; *((char**)item) = str; } } else { // scalar get_binary_item(prop->external_type, val); if (store_it) { item = elem_data + prop->offset; store_item(item, prop->internal_type, val, prop->external_type); } } } } /****************************************************************************** Write to a file the word that represents a PLY data type. Entry: code - code for type ******************************************************************************/ void PLY::write_scalar_type(int code) { // make sure this is a valid code if (code <= StartType || code >= EndType) abort_ply("error: write_scalar_type: bad data code = %d", code); // write the code to a file ostream->print("%s", write_type_names[code]); } /****************************************************************************** Get a text line from a file and break it up into words. IMPORTANT: The calling routine should call "free" on the returned pointer once finished with it. Entry: sfp - string file to read from Exit: nwords - number of words returned orig_line - the original line of characters returns a list of words from the line, or NULL if end-of-file ******************************************************************************/ char** PLY::get_words(STRISTREAM& sfp, int* nwords, char** orig_line) { const int BIG_STRING = 4096; char str[BIG_STRING]; char str_copy[BIG_STRING]; int max_words = 10; int num_words = 0; char *ptr, *ptr2; char** words = (char**)malloc(sizeof(char*) * max_words); // read in a line size_t len(sfp.readLine(str, BIG_STRING-2)); if (len == 0 || len == STREAM_ERROR) { *nwords = 0; *orig_line = NULL; free(words); return NULL; } // convert line-feed and tabs into spaces // (this guarantees that there will be a space before the // null character at the end of the string) if (str[len-1] == '\r') --len; str[len] = '\n'; for (ptr = str, ptr2 = str_copy; ; ptr++, ptr2++) { switch (*ptr) { case '\t': *ptr = ' '; *ptr2 = ' '; break; case '\n': *ptr = ' '; *(ptr+1) = *ptr2 = '\0'; goto EXIT_LOOP; default: *ptr2 = *ptr; } } EXIT_LOOP: // find the words in the line ptr = str; while (*ptr != '\0') { // jump over leading spaces while (*ptr == ' ') ptr++; // break if we reach the end if (*ptr == '\0') break; // allocate more room for words if necessary if (num_words >= max_words) { max_words += 10; words = (char**)realloc(words, sizeof(char*) * max_words); } if (*ptr == '\"') { // a quote indicates that we have a string // skip over leading quote ptr++; // save pointer to beginning of word words[num_words++] = ptr; // find trailing quote or end of line while (*ptr != '\"' && *ptr != '\0') ptr++; // replace quote with a null character to mark the end of the word // if we are not already at the end of the line if (*ptr != '\0') *ptr++ = '\0'; } else { // non-string // save pointer to beginning of word words[num_words++] = ptr; // jump over non-spaces while (*ptr != ' ') ptr++; // place a null character here to mark the end of the word *ptr++ = '\0'; } } // return the list of words *nwords = num_words; *orig_line = str_copy; return words; } /****************************************************************************** Write out an item to a file as raw binary bytes. Entry: val - item value to be written double_val - value type type - data type to write out ******************************************************************************/ void PLY::write_binary_item( const ValueType& val, int from_type, int to_type ) { switch (to_type) { case Int8: { const int8_t v(ValueType2Type(val, from_type)); ostream->write(&v, 1); break; } case Int16: { const int16_t v(ValueType2Type(val, from_type)); ostream->write(&v, 2); break; } case Int32: { const int32_t v(ValueType2Type(val, from_type)); ostream->write(&v, 4); break; } case Uint8: { const uint8_t v(ValueType2Type(val, from_type)); ostream->write(&v, 1); break; } case Uint16: { const uint16_t v(ValueType2Type(val, from_type)); ostream->write(&v, 2); break; } case Uint32: { const uint32_t v(ValueType2Type(val, from_type)); ostream->write(&v, 4); break; } case Float32: { const float v(ValueType2Type(val, from_type)); ostream->write(&v, 4); break; } case Float64: { const double v(ValueType2Type(val, from_type)); ostream->write(&v, 8); break; } default: abort_ply("error: write_binary_item: bad type = %d", to_type); } } /****************************************************************************** Write out an item to a file as ascii characters. Entry: val - item value to be written double_val - value type type - data type to write out ******************************************************************************/ void PLY::write_ascii_item( const ValueType& val, int from_type, int to_type ) { switch (to_type) { case Int8: case Int16: case Int32: ostream->print("%d ", ValueType2Type(val, from_type)); break; case Uint8: case Uint16: case Uint32: ostream->print("%u ", ValueType2Type(val, from_type)); break; case Float32: case Float64: ostream->print("%g ", ValueType2Type(val, from_type)); break; default: abort_ply("error: write_ascii_item: bad type = %d", to_type); } } /****************************************************************************** Get the value of an item that is in memory, and place the result into an integer, an unsigned integer and a double. Entry: ptr - pointer to the item type - data type supposedly in the item Exit: val - extracted value ******************************************************************************/ void PLY::get_stored_item( const void* ptr, int type, ValueType& val ) { switch (type) { case Int8: val.i8 = *((const int8_t*)ptr); break; case Uint8: val.u8 = *((const uint8_t*)ptr); break; case Int16: val.i16 = *((const int16_t*)ptr); break; case Uint16: val.u16 = *((const uint16_t*)ptr); break; case Int32: val.i32 = *((const int32_t*)ptr); break; case Uint32: val.u32 = *((const uint32_t*)ptr); break; case Float32: val.f = *((const float*)ptr); break; case Float64: val.d = *((const double*)ptr); break; default: abort_ply("error: get_stored_item: bad type = %d", type); } } /****************************************************************************** Get the value of an item from a binary file, and place the result into an integer, an unsigned integer and a double. Entry: type - data type supposedly in the word Exit: val - store value ******************************************************************************/ void PLY::get_binary_item(int type, ValueType& val) { switch (type) { case Int8: istream->read(&val.i8, 1); break; case Uint8: istream->read(&val.u8, 1); break; case Int16: istream->read(&val.i16, 2); break; case Uint16: istream->read(&val.u16, 2); break; case Int32: istream->read(&val.i32, 4); break; case Uint32: istream->read(&val.u32, 4); break; case Float32: istream->read(&val.f, 4); break; case Float64: istream->read(&val.d, 8); break; default: abort_ply("error: get_binary_item: bad type = %d", type); } } /****************************************************************************** Extract the value of an item from an ascii word, and place the result into an integer, an unsigned integer and a double. Entry: word - word to extract value from type - data type supposedly in the word Exit: val - store value ******************************************************************************/ void PLY::get_ascii_item(const char* word, int type, ValueType& val) { switch (type) { case Int8: val.i8 = (int8_t)atoi(word); break; case Uint8: val.u8 = (uint8_t)atoi(word); break; case Int16: val.i16 = (int16_t)atoi(word); break; case Uint16: val.u16 = (uint16_t)atoi(word); break; case Int32: val.i32 = atoi(word); break; case Uint32: val.u32 = strtoul(word, (char**)NULL, 10); break; case Float32: val.f = (float)atof(word); break; case Float64: val.d = atof(word); break; default: abort_ply("error: get_ascii_item: bad type = %d", type); } } /****************************************************************************** Store a value into a place being pointed to, guided by a data type. Entry: to_type - data type val - value to be stored from_type - value type Exit: ptr - data pointer to stored value ******************************************************************************/ void PLY::store_item( void* ptr, int to_type, const ValueType& val, int from_type ) { switch (to_type) { case Int8: *((int8_t*)ptr) = ValueType2Type(val, from_type); break; case Uint8: *((uint8_t*)ptr) = ValueType2Type(val, from_type); break; case Int16: *((int16_t*)ptr) = ValueType2Type(val, from_type); break; case Uint16: *((uint16_t*)ptr) = ValueType2Type(val, from_type); break; case Int32: *((int32_t*)ptr) = ValueType2Type(val, from_type); break; case Uint32: *((uint32_t*)ptr) = ValueType2Type(val, from_type); break; case Float32: *((float*)ptr) = ValueType2Type(val, from_type); break; case Float64: *((double*)ptr) = ValueType2Type(val, from_type); break; default: abort_ply("error: store_item: bad type = %d", to_type); } } /****************************************************************************** Add an element to a PLY file descriptor. Entry: words - list of words describing the element nwords - number of words in the list ******************************************************************************/ void PLY::add_element(const char** words, int /*nwords*/) { // create the new element PlyElement *elem = new PlyElement; elem->name = words[1]; elem->num = atoi(words[2]); // add the new element to the object's list elems.push_back(elem); } /****************************************************************************** Return the type of a property, given the name of the property. Entry: name - name of property type Exit: returns integer code for property, or 0 if not found ******************************************************************************/ int PLY::get_prop_type(const char* type_name) { // try to match the type name for (int i = StartType + 1; i < EndType; ++i) if (equal_strings (type_name, type_names[i])) return i; // see if we can match an old type name for (int i = StartType + 1; i < EndType; ++i) if (equal_strings (type_name, old_type_names[i])) return i; // if we get here, we didn't find the type return 0; } /****************************************************************************** Add a property to a PLY file descriptor. Entry: words - list of words describing the property nwords - number of words in the list ******************************************************************************/ void PLY::add_property(const char** words, int /*nwords*/) { // create the new property PlyProperty *prop = new PlyProperty; if (equal_strings(words[1], "list")) { // list prop->count_external = get_prop_type (words[2]); prop->external_type = get_prop_type (words[3]); prop->name = words[4]; prop->is_list = LIST; } else if (equal_strings(words[1], "string")) { // string prop->count_external = Int8; prop->external_type = Int8; prop->name = words[2]; prop->is_list = STRING; } else { // scalar prop->external_type = get_prop_type (words[1]); prop->name = words[2]; prop->is_list = SCALAR; } // internal types are the same as external by default prop->internal_type = prop->external_type; prop->count_internal = prop->count_external; // add this property to the list of properties of the current element PlyElement *elem = elems.back(); elem->props.push_back(prop); } /****************************************************************************** Add a comment to a PLY file descriptor. Entry: line - line containing comment ******************************************************************************/ void PLY::add_comment(const char* line) { // skip over "comment" and leading spaces and tabs int i = 7; while (line[i] == ' ' || line[i] == '\t') i++; append_comment(&line[i]); } /****************************************************************************** Add a some object information to a PLY file descriptor. Entry: line - line containing text info ******************************************************************************/ void PLY::add_obj_info(const char* line) { // skip over "obj_info" and leading spaces and tabs int i = 8; while (line[i] == ' ' || line[i] == '\t') i++; append_obj_info(&line[i]); } /****************************************************************************** Copy a property. ******************************************************************************/ void PLY::copy_property(PlyProperty& dest, const PlyProperty& src) { dest.name = src.name; dest.external_type = src.external_type; dest.internal_type = src.internal_type; dest.offset = src.offset; dest.is_list = src.is_list; dest.count_external = src.count_external; dest.count_internal = src.count_internal; dest.count_offset = src.count_offset; } /****************************************************************************** Return a list of the names of the elements in a particular PLY file. Entry: Exit: elem_names - the list of element names returns the number of elements ******************************************************************************/ int PLY::get_element_list(std::vector& elem_names) const { // create the list of element names elem_names.resize(elems.size()); for (size_t i = 0; i < elems.size(); ++i) elem_names[i] = elems[i]->name; // return the number of elements and the list of element names return (int)elems.size(); } /****************************************************************************** Append a comment to a PLY file. Entry: comment - the comment to append ******************************************************************************/ void PLY::append_comment(const char* comment) { // add comment to list comments.push_back(comment); } /****************************************************************************** Copy the comments from one PLY file to another. Entry: out_ply - destination file to copy comments to in_ply - the source of the comments ******************************************************************************/ void PLY::copy_comments(const PLY& in_ply) { for (size_t i = 0; i < in_ply.comments.size(); ++i) append_comment(in_ply.comments[i].c_str()); } /****************************************************************************** Append object information (arbitrary text) to a PLY file. Entry: obj_info - the object info to append ******************************************************************************/ void PLY::append_obj_info(const char* _obj_info) { // add info to list obj_info.push_back(_obj_info); } /****************************************************************************** Copy the object information from one PLY file to another. Entry: out_ply - destination file to copy object information to in_ply - the source of the object information ******************************************************************************/ void PLY::copy_obj_info(const PLY& in_ply) { for (size_t i = 0; i < in_ply.obj_info.size(); ++i) append_obj_info(in_ply.obj_info[i].c_str()); } /****************************************************************************** Specify the index of the next element to be read in from a PLY file. Entry: index - index of the element to be read Exit: elem_count - the number of elements in the file returns pointer to the name of this next element ******************************************************************************/ LPCSTR PLY::setup_element_read(int index, int* elem_count) { if ((size_t)index > elems.size()) { DEBUG("warning: No element with index %d", index); return 0; } PlyElement* elem = elems[index]; // set this to be the current element which_elem = elem; // return the number of such elements in the file and the element's name *elem_count = elem->num; return elem->name.c_str(); } /****************************************************************************** Specify one of several properties of the current element that is to be read from a file. This should be called (usually multiple times) before a call to the routine get_element(). Entry: prop - property to add to those that will be returned ******************************************************************************/ void PLY::setup_property(const PlyProperty& prop) { PlyElement *elem = which_elem; // deposit the property information into the element's description int index = find_property(elem, prop.name.c_str()); if (index == -1) { DEBUG("warning: Can't find property '%s' in element '%s'", prop.name.c_str(), elem->name.c_str()); return; } PlyProperty *prop_ptr = elem->props[index]; prop_ptr->internal_type = prop.internal_type; prop_ptr->offset = prop.offset; prop_ptr->count_internal = prop.count_internal; prop_ptr->count_offset = prop.count_offset; // specify that the user wants this property elem->store_prop[index] = STORE_PROP; } /****************************************************************************** Specify that we want the "other" properties of the current element to be tucked away within the user's structure. Entry: offset - offset to where other_props will be stored inside user's structure Exit: returns pointer to structure containing description of other_props ******************************************************************************/ PLY::PlyOtherProp* PLY::get_other_properties(int offset) { return get_other_properties(which_elem, offset); } /****************************************************************************** Describe which element is to be written next and state how many of them will be written. Entry: elem_name - name of element that information is being described nelems - number of elements of this type to be written ******************************************************************************/ void PLY::describe_element(const char* elem_name, int nelems) { // look for appropriate element PlyElement *elem = find_element(elem_name); if (elem == NULL) abort_ply("error: describe_element: can't find element '%s'",elem_name); elem->num = nelems; // now this element is the current element which_elem = elem; } /****************************************************************************** Describe a property of an element. Entry: prop - the new property ******************************************************************************/ void PLY::describe_property(const PlyProperty& prop) { PlyElement *elem = which_elem; // copy the new property PlyProperty *elem_prop = new PlyProperty; copy_property(*elem_prop, prop); elem->props.push_back(elem_prop); elem->store_prop.push_back(NAMED_PROP); } /****************************************************************************** Describe what the "other" properties are that are to be stored, and where they are in an element. ******************************************************************************/ void PLY::describe_other_properties( PlyOtherProp *other, int offset ) { // look for appropriate element PlyElement *elem = find_element(other->name.c_str()); if (elem == NULL) { DEBUG("warning: describe_other_properties: can't find element '%s'", other->name.c_str()); return; } // copy the other properties for (size_t i = 0; i < other->props.size(); ++i) { PlyProperty *prop = new PlyProperty; copy_property(*prop, *other->props[i]); elem->props.push_back(prop); elem->store_prop.push_back(OTHER_PROP); } // save other info about other properties elem->other_size = other->size; elem->other_offset = offset; } /****************************************************************************** Pass along a pointer to "other" elements that we want to save in a given PLY file. These other elements were presumably read from another PLY file. Entry: other_elems - info about other elements that we want to store ******************************************************************************/ void PLY::describe_other_elements(PlyOtherElems* _other_elems) { // ignore this call if there is no other element if (_other_elems == NULL) return; // save pointer to this information other_elems = _other_elems; // describe the other properties of this element for (size_t i = 0; i < other_elems->other_list.size(); ++i) { OtherElem *other = &(other_elems->other_list[i]); element_count(other->elem_name.c_str(), other->elem_count); describe_other_properties(other->other_props, offsetof(OtherData,other_props)); } } /****************************************************************************** Initialize the property propagation rules for an element. Default is to use averaging (AVERAGE_RULE) for creating all new properties. Entry: elem_name - name of the element that we're making the rules for Exit: returns pointer to the default rules ******************************************************************************/ PLY::PlyPropRules* PLY::init_rule(const char* elem_name) { PlyElement *elem = find_element(elem_name); if (elem == NULL) abort_ply("error: init_rule: Can't find element '%s'", elem_name); PlyPropRules *rules = new PlyPropRules; rules->elem = elem; rules->max_props = 0; rules->rule_list = NULL; // see if there are other rules we should use if (elem->props.empty()) return rules; // default is to use averaging rule rules->rule_list = new int[elem->props.size()]; for (size_t i = 0; i < elem->props.size(); ++i) rules->rule_list[i] = AVERAGE_RULE; // try to match the element, property and rule name for (PlyRuleList *list = rule_list; list != NULL; list = list->next) { if (!equal_strings(list->element, elem->name.c_str())) continue; int found_prop = 0; for (size_t i = 0; i < elem->props.size(); ++i) if (equal_strings(list->property, elem->props[i]->name.c_str())) { found_prop = 1; // look for matching rule name for (int j = 0; rule_name_list[j].code != -1; ++j) if (equal_strings(list->name, rule_name_list[j].name.c_str())) { rules->rule_list[i] = rule_name_list[j].code; break; } } if (!found_prop) { DEBUG("warning: Can't find property '%s' for rule '%s'", list->property, list->name); continue; } } return rules; } /****************************************************************************** Modify a property propagation rule. Entry: rules - rules for the element prop_name - name of the property whose rule we're modifying rule_type - type of rule (MAXIMUM_RULE, MINIMUM_RULE, MAJORITY_RULE, etc.) ******************************************************************************/ void PLY::modify_rule(PlyPropRules* rules, const char* prop_name, int rule_type) { PlyElement *elem = rules->elem; // find the property and modify its rule type for (size_t i = 0; i < elem->props.size(); ++i) if (equal_strings(elem->props[i]->name.c_str(), prop_name)) { rules->rule_list[i] = rule_type; return; } // we didn't find the property if we get here abort_ply("error: modify_rule: Can't find property '%s'", prop_name); } /****************************************************************************** Begin to create a set of properties from a set of propagation rules. Entry: rules - rules for the element ******************************************************************************/ void PLY::start_props(PlyPropRules* rules) { // save pointer to the rules in the PLY object current_rules = rules; } /****************************************************************************** Remember a set of properties and their weights for creating a new set of properties. Entry: weight - weights for this set of properties other_props - the properties to use ******************************************************************************/ void PLY::weight_props(float weight, void* other_props) { PlyPropRules *rules = current_rules; // allocate space for properties and weights, if necessary if (rules->max_props == 0) { rules->max_props = 6; } if (rules->props.size() == rules->max_props) { rules->max_props *= 2; } rules->props.reserve(rules->max_props); rules->weights.reserve(rules->max_props); // remember these new properties and their weights rules->props.push_back(other_props); rules->weights.push_back(weight); } /****************************************************************************** Return a pointer to a new set of properties that have been created using a specified set of property combination rules and a given collection of "other" properties. Exit: returns a pointer to the new properties ******************************************************************************/ void* PLY::get_new_props() { PlyPropRules *rules = current_rules; PlyElement *elem = rules->elem; PlyProperty *prop; int offset; int type; ValueType val; // return NULL if we've got no "other" properties if (elem->other_size == 0) return NULL; // create room for combined other properties char *new_data = new char[elem->other_size]; // make sure there is enough room to store values we're to combine vals.resize(rules->props.size()); // calculate the combination for each "other" property of the element for (size_t i = 0; i < elem->props.size(); ++i) { // don't bother with properties we've been asked to store explicitly if (elem->store_prop[i]) continue; prop = elem->props[i]; offset = prop->offset; type = prop->external_type; // collect together all the values we're to combine for (size_t j = 0; j < rules->props.size(); ++j) { char* data = (char*)rules->props[j]; void* ptr = (void *)(data + offset); get_stored_item((void*)ptr, type, val); vals[j] = ValueType2Type(val, type); } // calculate the combined value switch (rules->rule_list[i]) { case AVERAGE_RULE: { double sum = 0; double weight_sum = 0; for (size_t j = 0; j < rules->props.size(); ++j) { sum += vals[j] * rules->weights[j]; weight_sum += rules->weights[j]; } val.d = sum / weight_sum; break; } case MINIMUM_RULE: { val.d = vals[0]; for (size_t j = 1; j < rules->props.size(); ++j) if (val.d > vals[j]) val.d = vals[j]; break; } case MAXIMUM_RULE: { val.d = vals[0]; for (size_t j = 1; j < rules->props.size(); ++j) if (val.d < vals[j]) val.d = vals[j]; break; } case RANDOM_RULE: { val.d = vals[FLOOR2INT(SEACAVE::random() * rules->props.size())]; break; } case SAME_RULE: { val.d = vals[0]; for (size_t j = 1; j < rules->props.size(); ++j) if (val.d != vals[j]) abort_ply("error: get_new_props: Error combining properties that should be the same"); break; } default: abort_ply("error: get_new_props: Bad rule = %d", rules->rule_list[i]); } // store the combined value store_item(new_data + offset, type, val, Float64); } return new_data; } /****************************************************************************** Set the list of user-specified property combination rules. ******************************************************************************/ void PLY::set_prop_rules(PlyRuleList* prop_rules) { rule_list = prop_rules; } /****************************************************************************** Append a property rule to a growing list of user-specified rules. Entry: rule_list - current rule list name - name of property combination rule property - "element.property" says which property the rule affects Exit: returns pointer to the new rule list ******************************************************************************/ PLY::PlyRuleList* PLY::append_prop_rule( PlyRuleList* rule_list, const char* name, const char* property ) { char *str2; char *ptr; // find . char *str = strdup(property); for (ptr = str; *ptr != '\0' && *ptr != '.'; ptr++) ; // split string at . if (*ptr == '.') { *ptr = '\0'; str2 = ptr + 1; } else { DEBUG("warning: Can't find property '%s' for rule '%s'", property, name); return rule_list; } PlyRuleList *rule = new PlyRuleList; rule->name = name; rule->element = str; rule->property = str2; rule->next = NULL; // either start rule list or append to it if (rule_list == NULL) rule_list = rule; else { // append new rule to current list PlyRuleList *rule_ptr = rule_list; while (rule_ptr->next != NULL) rule_ptr = rule_ptr->next; rule_ptr->next = rule; } // return pointer to list return rule_list; } /****************************************************************************** See if a name matches the name of any property combination rules. Entry: name - name of rule we're trying to match Exit: returns 1 if we find a match, 0 if not ******************************************************************************/ int PLY::matches_rule_name(const char* name) { for (int i = 0; rule_name_list[i].code != -1; ++i) if (equal_strings(rule_name_list[i].name.c_str(), name)) return 1; return 0; }