You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2386 lines
65 KiB

////////////////////////////////////////////////////////////////////
// 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<Stream*>(istream)->getInputStream(ISTREAM::LAYER_ID_IN)) {
IOSTREAM* const iostream(static_cast<Stream*>(istream)->getIOStream(ISTREAM::LAYER_ID_IN, OSTREAM::LAYER_ID_OUT));
if (iostream)
delete iostream;
else
delete istream;
} else {
ASSERT(static_cast<Stream*>(ostream)->getOutputStream(OSTREAM::LAYER_ID_OUT));
IOSTREAM* const iostream(static_cast<Stream*>(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; i<elems.size(); ++i) {
PlyElement* elem = elems[i];
if (!elem->props.empty()) {
for (size_t j=0; j<elem->props.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; i<other_elems->other_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<true>(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; i<nprops; ++i)
describe_property(props[i]);
}
/******************************************************************************
State how many of a given element will be written.
Entry:
elem_name - name of element that information is being specified about
nelems - number of elements of this type to be written
******************************************************************************/
void PLY::element_count(const char* elem_name, int nelems)
{
// look for appropriate element
PlyElement *elem = find_element(elem_name);
if (elem == NULL)
abort_ply("error: element_count: can't find element '%s'", elem_name);
elem->num = 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<true>(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<int>(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<int>(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<true>(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<PlyProperty*>& 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<std::string>& 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<std::string>& 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; i<elems.size(); ++i)
if (equal_strings(element, elems[i]->name.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; i<elem->props.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<int>(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<int>(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<int8_t>(val, from_type));
ostream->write(&v, 1);
break; }
case Int16: {
const int16_t v(ValueType2Type<int16_t>(val, from_type));
ostream->write(&v, 2);
break; }
case Int32: {
const int32_t v(ValueType2Type<int32_t>(val, from_type));
ostream->write(&v, 4);
break; }
case Uint8: {
const uint8_t v(ValueType2Type<uint8_t>(val, from_type));
ostream->write(&v, 1);
break; }
case Uint16: {
const uint16_t v(ValueType2Type<uint16_t>(val, from_type));
ostream->write(&v, 2);
break; }
case Uint32: {
const uint32_t v(ValueType2Type<uint32_t>(val, from_type));
ostream->write(&v, 4);
break; }
case Float32: {
const float v(ValueType2Type<float>(val, from_type));
ostream->write(&v, 4);
break; }
case Float64: {
const double v(ValueType2Type<double>(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<int32_t>(val, from_type));
break;
case Uint8:
case Uint16:
case Uint32:
ostream->print("%u ", ValueType2Type<uint32_t>(val, from_type));
break;
case Float32:
case Float64:
ostream->print("%g ", ValueType2Type<double>(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<int8_t>(val, from_type);
break;
case Uint8:
*((uint8_t*)ptr) = ValueType2Type<uint8_t>(val, from_type);
break;
case Int16:
*((int16_t*)ptr) = ValueType2Type<int16_t>(val, from_type);
break;
case Uint16:
*((uint16_t*)ptr) = ValueType2Type<uint16_t>(val, from_type);
break;
case Int32:
*((int32_t*)ptr) = ValueType2Type<int32_t>(val, from_type);
break;
case Uint32:
*((uint32_t*)ptr) = ValueType2Type<uint32_t>(val, from_type);
break;
case Float32:
*((float*)ptr) = ValueType2Type<float>(val, from_type);
break;
case Float64:
*((double*)ptr) = ValueType2Type<double>(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<std::string>& 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<double>(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;
}