2387 lines
65 KiB
C++
2387 lines
65 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// 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;
|
|
}
|