Skip to content

Commit

Permalink
cpp: Add printf and houdini padding syntax support (refs #14)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinfx committed Dec 1, 2020
1 parent cc672ff commit f4c602d
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 2 deletions.
4 changes: 4 additions & 0 deletions cpp/docs/FileseqAPIMain.dox
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Comma Delimited: 1-10,10-20
Staggered: 1-100:3 (1-100x3, 1-100x2, 1-100)
Negative Frames: -10-100
Padding: #=4 padded, @=single pad
Printf Padding: %04d=4 padded, %01d=1 padded
Houdini Padding: $F4=4 padding, $F=1 padded
\endverbatim

---
Expand All @@ -32,6 +34,8 @@ Sequences of files are expected to follow a pattern similar to:
/path/to/some/file_foo.0100.exr
/path/to/some/file_foo.1-100#.jpg
/path/to/some/file_foo.1-100@@@.tif
/path/to/some/file_foo.1-100%04d.tif
/path/to/some/file_foo.1-100$F04.tif
\endverbatim

\page CHANGES CHANGES
Expand Down
14 changes: 14 additions & 0 deletions cpp/pad.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "pad.h"
#include "private/frameset_p.h"
#include "private/sequence_p.h"

#include <algorithm>
#include <sstream>
Expand Down Expand Up @@ -125,8 +126,21 @@ std::string PaddingMapper::getAllChars() const {
}

size_t PaddingMapper::getPaddingCharsSize(const std::string &chars) const {
if (chars.empty()) {
return 0;
}

size_t size = 0;

// check for alternate padding syntax
if ((size = internal::getPadSize(chars, internal::PadSyntaxPrintf)) > 0) {
return size;
}
if ((size = internal::getPadSize(chars, internal::PadSyntaxHoudini)) > 0) {
return size;
}

// standard pad chars
CharSizeMap::const_iterator found;
std::string::const_iterator strIt;

Expand Down
76 changes: 75 additions & 1 deletion cpp/private/sequence_p.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#include "sequence_p.h"

#include <iostream>
#include <string>
#include <vector>

Expand All @@ -21,7 +22,13 @@ bool getSplitPatternMatch(SeqPatternMatch &match, const std::string &path) {
// Example:
// /film/shot/renders/hero_bty.1-100#.exr
// /film/shot/renders/hero_bty.@@.exr
static const char* s_pattern = R"(^(.*?)([\d-][:xy\d,-]*)?([#@]+)((?:\.[a-zA-Z0-9]+)*)$)";
// /film/shot/renders/hero_bty.%02d.exr
// /film/shot/renders/hero_bty.$F02.exr
static const char* s_pattern =
R"(^(.*?))" // dir and basename
R"(([\d-][:xy\d,-]*)?)" // optional frame range
R"(([#@]+|%\d*d|\$F\d*))" // padding: chars, printf, houdini
R"(((?:\.[a-zA-Z0-9]+)*)$)"; // extension

match.base.clear();
match.range.clear();
Expand Down Expand Up @@ -85,6 +92,73 @@ bool getSingleFrameMatch(SeqPatternMatch &match, const std::string &path) {
}


size_t getPadSize(const std::string &pad, PadSyntax syntax) {
// printf: %04d
static const char *s_printf_pattern = R"(^%(\d*)d$)";
// houdini: $F, $F4, $F04
static const char *s_houdini_pattern = R"(^\$F(\d*)$)";

if (pad.empty()) {
return 0;
}

size_t val = 0;

#if HAVE_REGEX == 1
static const auto flags = std::regex_constants::optimize|std::regex_constants::ECMAScript;
static const std::regex s_printf_rx(s_printf_pattern, flags);
static const std::regex s_houdini_rx(s_houdini_pattern, flags);

const std::regex* rx;
switch (syntax) {
case PadSyntaxPrintf:
rx = &s_printf_rx;
break;
case PadSyntaxHoudini:
rx = &s_houdini_rx;
break;
default:
std::cerr << "warning: unhandled fileseq pad format "
"syntax getPadSize(syntax=" << syntax << ")\n";
return val;
}

std::smatch submatch;
if (!std::regex_match(pad, submatch, *rx)) {
return val;
}
val = !submatch[1].str().empty() ? std::stol(submatch[1].str()) : 1;

#else
static const pcrecpp::RE* s_printf_rx = new pcrecpp::RE(s_printf_pattern);
static const pcrecpp::RE* s_houdini_rx = new pcrecpp::RE(s_houdini_pattern);

const pcrecpp::RE* rx;
switch (syntax) {
case PadSyntaxPrintf:
rx = s_printf_rx;
break;
case PadSyntaxHoudini:
rx = s_houdini_rx;
break;
default:
std::cerr << "warning: unhandled fileseq pad format "
"syntax getPadSize(syntax=" << syntax << ")\n";
return val;
}

std::string str;
if (!(rx->FullMatch(pad, &str))) {
return val;
}
val = !str.empty() ? std::stol(str) : 1;

#endif

return std::max(val, size_t(1));
}


} // internal

} // fileseq
10 changes: 10 additions & 0 deletions cpp/private/sequence_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ bool getSplitPatternMatch(SeqPatternMatch &match, const std::string &path);
// Returns true if the match was successful.
bool getSingleFrameMatch(SeqPatternMatch &match, const std::string &path);

// PadSyntax indicate alternate padding format syntax
enum PadSyntax {
PadSyntaxPrintf = 1, // %04d
PadSyntaxHoudini = 2, // $F, $F4, $F04
};

// Try to match padding syntax, and return the parsed padding size.
// Returns 0 if there is no match.
size_t getPadSize(const std::string &pad, PadSyntax syntax);

// Private data container for FileSequence
class FileSequenceData {

Expand Down
36 changes: 36 additions & 0 deletions cpp/test/TestFileSequence.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ class TestNewFileSequences : public testing::Test {
Case t = {"/dir/f.1-100@@@.f", "/dir/f.1-100@@@.f", 1, 100, 3, 100, ".f"};
m_cases.push_back(t);
}
{
Case t = {"/dir/f.1-100%03d.f", "/dir/f.1-100%03d.f", 1, 100, 3, 100, ".f"};
m_cases.push_back(t);
}
{
Case t = {"/dir/f.1-100$F03.f", "/dir/f.1-100$F03.f", 1, 100, 3, 100, ".f"};
m_cases.push_back(t);
}
{
Case t = {
"/dir/f.1-10,50,60-90x2##.mp4",
Expand Down Expand Up @@ -186,6 +194,14 @@ class TestNewFileSequenceFrameIndex : public testing::Test {
Case t = {"file.1-100#.exr", 100, 4, 5, "file.0005.exr"};
m_cases.push_back(t);
}
{
Case t = {"file.1-100%04d.exr", 100, 4, 5, "file.0005.exr"};
m_cases.push_back(t);
}
{
Case t = {"file.1-100$F04.exr", 100, 4, 5, "file.0005.exr"};
m_cases.push_back(t);
}
{
Case t = {"file.-10-30@@@.exr", 40, 25, 15, "file.015.exr"};
m_cases.push_back(t);
Expand Down Expand Up @@ -254,6 +270,22 @@ class TestNewFileSequenceSetDir : public testing::Test {
};
m_cases.push_back(t);
}
{
Case t = {
"/path/to/file.1-100%04d.exr",
"/other/subdir", "fileB", "f", "-10-5,20-30x2", "$F3",
"/other/subdir/fileB-10-5,20-30x2$F3.f"
};
m_cases.push_back(t);
}
{
Case t = {
"/path/to/file.1-100$F3.exr",
"/other/subdir", "fileB", "f", "-10-5,20-30x2", "%04d",
"/other/subdir/fileB-10-5,20-30x2%04d.f"
};
m_cases.push_back(t);
}
}

std::vector<Case> m_cases;
Expand Down Expand Up @@ -347,6 +379,10 @@ class TestSplitPatternChars : public testing::Test {
void SetUp() {
{Case t = {"/path/to/file%s1-1x1#.exr", "file%s", 1, 1, "#"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s_1-100x10@@.exr", "file%s_", 1, 91, "@@"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s_1-100x10%02d.exr", "file%s_", 1, 91, "%02d"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s_1-100x10$F.exr", "file%s_", 1, 91, "$F"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s_1-100x10$F2.exr", "file%s_", 1, 91, "$F2"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s_1-100x10$F02.exr", "file%s_", 1, 91, "$F02"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s.-10--1x2##.exr", "file%s.", -10, -2, "##"}; m_cases.push_back(t);}
{Case t = {"/path/to/file%s1,2,3,5-10,20-30#.exr", "file%s", 1, 30, "#"}; m_cases.push_back(t);}
}
Expand Down
30 changes: 29 additions & 1 deletion cpp/test/TestFunctions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ class TestPaddingCharsSize : public testing::Test {
{Case t = {PadStyleHash1, "@@", 2}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "@@@", 3}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "@@@@", 4}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "%01d", 1}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "%04d", 4}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "$F", 1}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "$F2", 2}; m_cases.push_back(t);}
{Case t = {PadStyleHash1, "$F04", 4}; m_cases.push_back(t);}

{Case t = {PadStyleHash4, "", 0}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "#", 4}; m_cases.push_back(t);}
Expand All @@ -156,6 +161,11 @@ class TestPaddingCharsSize : public testing::Test {
{Case t = {PadStyleHash4, "@@", 2}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "@@@", 3}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "@@@@", 4}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "%01d", 1}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "%04d", 4}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "$F", 1}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "$F2", 2}; m_cases.push_back(t);}
{Case t = {PadStyleHash4, "$F04", 4}; m_cases.push_back(t);}
}

std::vector<Case> m_cases;
Expand Down Expand Up @@ -214,6 +224,9 @@ void testFindSequencesOnDisk(bool singleFiles) {
}
// Found
mapIt->second = true;

// Sanity check
EXPECT_NE("", seqs[i].index(0));
}

for (mapIt = cases.begin(); mapIt != cases.end(); ++mapIt) {
Expand Down Expand Up @@ -241,7 +254,6 @@ TEST( TestFindSequencesOnDisk, HandleSymlinksOnDisk ) {
ASSERT_EQ(expected, seqs[0].string());
}


class TestFindSequenceOnDisk : public testing::Test {

public:
Expand Down Expand Up @@ -284,6 +296,14 @@ class TestFindSequenceOnDisk : public testing::Test {
Case t = {PadStyleHash1, "testdata/seqA.@.jpg", ""};
m_cases.push_back(t);
}
{
Case t = {PadStyleHash1, "testdata/seqA.%04d.exr", "testdata/seqA.1,3-6,8-10####.exr"};
m_cases.push_back(t);
}
{
Case t = {PadStyleHash1, "testdata/seqA.$F4.exr", "testdata/seqA.1,3-6,8-10####.exr"};
m_cases.push_back(t);
}

// PadStyleHash4
{
Expand Down Expand Up @@ -314,6 +334,14 @@ class TestFindSequenceOnDisk : public testing::Test {
Case t = {PadStyleHash4, "testdata/seqA.@.jpg", ""};
m_cases.push_back(t);
}
{
Case t = {PadStyleHash4, "testdata/seqA.%04d.exr", "testdata/seqA.1,3-6,8-10#.exr"};
m_cases.push_back(t);
}
{
Case t = {PadStyleHash4, "testdata/seqA.$F4.exr", "testdata/seqA.1,3-6,8-10#.exr"};
m_cases.push_back(t);
}
}

std::vector<Case> m_cases;
Expand Down

0 comments on commit f4c602d

Please sign in to comment.