Skip to content

Commit

Permalink
Add support for user-specified dimension types.
Browse files Browse the repository at this point in the history
Write integers using integer formatting.
  • Loading branch information
abellgithub committed Apr 16, 2019
1 parent fc5e6bb commit c668910
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 31 deletions.
11 changes: 10 additions & 1 deletion doc/stages/writers.ply.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,21 @@ storage_mode
of the machine. [Default: "ascii"]

dims
List of dimensions to write as elements. [Default: all dimensions]
List of dimensions (and sizes) in the format <dimension_name>[=<type>],...
to write as output. (e.g., "Y=int32_t, X,Red=char")
[Default: All dimensions with stored types]

faces
Write a mesh as faces in addition to writing points as vertices.
[Default: false]

sized_types
PLY has variously been written with explicitly sized type strings
('int8', 'float32", 'uint32', etc.) and implied sized type strings
('char', 'float', 'int', etc.). If true, explicitly sized type strings
are used. If false, implicitly sized type strings are used.
[Default: true]

precision
If specified, the number of digits to the right of the decimal place
using f-style formatting. Only permitted when 'storage_mode' is 'ascii'.
Expand Down
130 changes: 112 additions & 18 deletions io/PlyWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,58 @@ void PlyWriter::addArgs(ProgramArgs& args)
args.add("storage_mode", "PLY Storage Mode", m_format, Format::Ascii);
args.add("dims", "Dimension names", m_dimNames);
args.add("faces", "Write faces", m_faces);
args.add("sized_types",
"Write types as size-explicit strings (e.g. 'uint32')",
m_sizedTypes, true);
m_precisionArg = &args.add("precision", "Output precision", m_precision, 3);
}


void PlyWriter::prepared(PointTableRef table)
{
PointLayoutPtr layout = table.layout();

if (m_precisionArg->set() && m_format != Format::Ascii)
throwError("Option 'precision' can only be set of the 'storage_mode' "
"is ascii.");
if (m_dimNames.size())
{
for (auto& name : m_dimNames)
for (auto& dimspec : m_dimNames)
{
auto id = table.layout()->findDim(name);
Dimension::Type type;
StringList parts = Utils::split2(dimspec, '=');
std::string name = parts[0];
Utils::trim(name);
dimspec = Utils::tolower(name);
auto id = layout->findDim(name);
if (id == Dimension::Id::Unknown)
throwError("Unknown dimension '" + name + "' in provided "
"dimension list.");
m_dims.push_back(id);
if (parts.size() == 1)
type = layout->dimType(id);
else if (parts.size() == 2)
{
Utils::trim(parts[1]);
type = Dimension::type(parts[1]);
}
else
throwError("Invalid format for dimension specification for "
"dimension '" + name + "'. " "Must be 'name[=type]'.");
m_dims.emplace_back(id, type);
}
}
else
{
m_dims = table.layout()->dims();
for (auto dim : m_dims)
m_dimNames.push_back(Utils::tolower(table.layout()->dimName(dim)));
m_dims = layout->dimTypes();
for (const auto& dim : m_dims)
m_dimNames.push_back(Utils::tolower(layout->dimName(dim.m_id)));
}
}


std::string PlyWriter::getType(Dimension::Type type) const
{
static std::map<Dimension::Type, std::string> types =
static std::map<Dimension::Type, std::string> sizedTypes =
{
{ Dimension::Type::Signed8, "int8" },
{ Dimension::Type::Unsigned8, "uint8" },
Expand All @@ -108,10 +128,21 @@ std::string PlyWriter::getType(Dimension::Type type) const
{ Dimension::Type::Float, "float32" },
{ Dimension::Type::Double, "float64" }
};
static std::map<Dimension::Type, std::string> types =
{
{ Dimension::Type::Signed8, "char" },
{ Dimension::Type::Unsigned8, "uchar" },
{ Dimension::Type::Signed16, "short" },
{ Dimension::Type::Unsigned16, "ushort" },
{ Dimension::Type::Signed32, "int" },
{ Dimension::Type::Unsigned32, "uint" },
{ Dimension::Type::Float, "float" },
{ Dimension::Type::Double, "double" }
};

try
{
return types.at(type);
return m_sizedTypes ? sizedTypes.at(type) : types.at(type);
}
catch (std::out_of_range&)
{
Expand All @@ -133,7 +164,7 @@ void PlyWriter::writeHeader(PointLayoutPtr layout) const
for (auto dim : m_dims)
{
std::string name = *ni++;
std::string typeString = getType(layout->dimType(dim));
std::string typeString = getType(dim.m_type);
*m_stream << "property " << typeString << " " << name << std::endl;
}
if (m_faces)
Expand Down Expand Up @@ -163,21 +194,85 @@ void PlyWriter::write(const PointViewPtr data)
}


template<typename T>
void writeTextVal(std::ostream& out, PointRef& point, Dimension::Id dim)
{
T t = point.getFieldAs<T>(dim);
out << t;
}

// Writing int/uint can write "char" type instead of numeric values.
// Two specializations is easier than the SFINAE mess.
template<>
void writeTextVal<int8_t>(std::ostream& out, PointRef& point, Dimension::Id dim)
{
int i = point.getFieldAs<int8_t>(dim);
out << i;
}

template<>
void writeTextVal<uint8_t>(std::ostream& out, PointRef& point,
Dimension::Id dim)
{
uint32_t i = point.getFieldAs<uint8_t>(dim);
out << i;
}


void PlyWriter::writeValue(PointRef& point, Dimension::Id dim,
Dimension::Type type)
{
if (m_format == Format::Ascii)
{
double d = point.getFieldAs<double>(dim);
if (m_precisionArg->set() &&
Dimension::base(type) == Dimension::BaseType::Floating)
if (Dimension::base(type) == Dimension::BaseType::Floating)
{
*m_stream << std::fixed;
m_stream->precision(m_precision);
if (m_precisionArg->set())
{
*m_stream << std::fixed;
m_stream->precision(m_precision);
}

if (type == Dimension::Type::Float)
writeTextVal<float>(*m_stream, point, dim);
else
writeTextVal<double>(*m_stream, point, dim);

if (m_precisionArg->set())
m_stream->unsetf(std::ios_base::fixed);
}
else
m_stream->unsetf(std::ios_base::fixed);
*m_stream << d;
{
switch (type)
{
case Dimension::Type::Unsigned8:
writeTextVal<uint8_t>(*m_stream, point, dim);
break;
case Dimension::Type::Unsigned16:
writeTextVal<uint16_t>(*m_stream, point, dim);
break;
case Dimension::Type::Unsigned32:
writeTextVal<uint32_t>(*m_stream, point, dim);
break;
case Dimension::Type::Unsigned64:
writeTextVal<uint64_t>(*m_stream, point, dim);
break;
case Dimension::Type::Signed8:
writeTextVal<int8_t>(*m_stream, point, dim);
break;
case Dimension::Type::Signed16:
writeTextVal<int16_t>(*m_stream, point, dim);
break;
case Dimension::Type::Signed32:
writeTextVal<int32_t>(*m_stream, point, dim);
break;
case Dimension::Type::Signed64:
writeTextVal<int64_t>(*m_stream, point, dim);
break;
default:
throwError("Internal error: invalid type found writing "
"output.");
}
}
}
else if (m_format == Format::BinaryLe)
{
Expand All @@ -200,8 +295,7 @@ void PlyWriter::writePoint(PointRef& point, PointLayoutPtr layout)
{
for (auto it = m_dims.begin(); it != m_dims.end();)
{
Dimension::Id dim = *it;
writeValue(point, dim, layout->dimType(dim));
writeValue(point, it->m_id, it->m_type);
++it;
if (m_format == Format::Ascii && it != m_dims.end())
*m_stream << " ";
Expand Down
3 changes: 2 additions & 1 deletion io/PlyWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ class PDAL_DLL PlyWriter : public Writer
Format m_format;
bool m_faces;
StringList m_dimNames;
Dimension::IdList m_dims;
DimTypeList m_dims;
int m_precision;
bool m_sizedTypes;
Arg *m_precisionArg;
std::vector<PointViewPtr> m_views;
};
Expand Down
20 changes: 10 additions & 10 deletions pdal/DimUtil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,25 +147,25 @@ inline Type type(std::string s)
{
s = Utils::tolower(s);

if (s == "int8_t" || s == "int8")
if (s == "int8_t" || s == "int8" || s == "char")
return Type::Signed8;
if (s == "int16_t" || s == "int16")
if (s == "int16_t" || s == "int16" || s == "short")
return Type::Signed16;
if (s == "int32_t" || s == "int32")
if (s == "int32_t" || s == "int32" || s == "int")
return Type::Signed32;
if (s == "int64_t" || s == "int64")
if (s == "int64_t" || s == "int64" || s == "long")
return Type::Signed64;
if (s == "uint8_t" || s == "uint8")
if (s == "uint8_t" || s == "uint8" || s == "uchar")
return Type::Unsigned8;
if (s == "uint16_t" || s == "uint16")
if (s == "uint16_t" || s == "uint16" || s == "ushort")
return Type::Unsigned16;
if (s == "uint32_t" || s == "uint32")
if (s == "uint32_t" || s == "uint32" || s == "uint")
return Type::Unsigned32;
if (s == "uint64_t" || s == "uint64")
if (s == "uint64_t" || s == "uint64" || s == "ulong")
return Type::Unsigned64;
if (s == "float")
if (s == "float" || s == "float32")
return Type::Float;
if (s == "double")
if (s == "double" || s == "float64")
return Type::Double;
return Type::None;
}
Expand Down
2 changes: 1 addition & 1 deletion test/data/ply/issue_2421.ply
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ property float64 y
property float64 z
property int32 i
end_header
1.23457 12345.67890 1234567890.12345 12345
1.23457 12345.67890 1234567890.12345 1234567890
10 changes: 10 additions & 0 deletions test/data/ply/sized_dims.ply
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ply
format ascii 1.0
comment Generated by PDAL
element vertex 1
property int8 x
property uint16 y
property int32 j
property float64 i
end_header
1 12346 12345 1234567890.00000
10 changes: 10 additions & 0 deletions test/data/ply/unsized_dims.ply
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ply
format ascii 1.0
comment Generated by PDAL
element vertex 1
property ushort y
property char x
property double i
property int j
end_header
12346 1 1234567890.00000 12345

0 comments on commit c668910

Please sign in to comment.