|
Shinya Kitaoka |
e5734a |
#include <memory></memory>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#include "tropcm.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// TnzCore includes
|
|
Toshihiro Shimizu |
890ddd |
#include "traster.h"
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
// STD includes
|
|
Toshihiro Shimizu |
890ddd |
#include <limits></limits>
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
//#define UNIT_TEST // Enables unit
|
|
Shinya Kitaoka |
38fd86 |
// testing at program startup
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Rationale
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\file tdistancetransform.cpp
|
|
Toshihiro Shimizu |
890ddd |
\brief This file implements an O(rows * cols) 2-dimensional distance
|
|
Toshihiro Shimizu |
890ddd |
transform algorithm with customizable action on squared pixel
|
|
Toshihiro Shimizu |
890ddd |
distance from the closest pixel.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Local namespace stuff
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
namespace {
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Given 2 parabolas with (minimal) height at centers \p a and \p b
|
|
Toshihiro Shimizu |
890ddd |
and centers separated by distance \p d, returns the min between
|
|
Shinya Kitaoka |
120a6e |
\p d and the value \p x satisfying <tt>a + x^2 == b + (x -</tt>
|
|
Shinya Kitaoka |
120a6e |
d)^2.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
unsigned int takeoverDist(unsigned int a, unsigned int b, unsigned int d) {
|
|
Shinya Kitaoka |
120a6e |
// The actual formula is: x = (h^2 + b - a) / 2h. It simplifies as follows
|
|
Shinya Kitaoka |
120a6e |
// using integers only.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
// NOTE: It can be proven that with integer division, x/ab == (x/a)/b.
|
|
Shinya Kitaoka |
120a6e |
return (b < a) ? d : std::max((d + (b - a) / d + 1) / 2,
|
|
Shinya Kitaoka |
120a6e |
d); // Note the +1 to get the ceil
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename isinsidefunc="" pix,="" typename=""></typename>
|
|
Shinya Kitaoka |
120a6e |
void initializeDT(const TRasterPT<pix> &ras,</pix>
|
|
Shinya Kitaoka |
120a6e |
const TRasterPT<unsigned int=""> &dtRas, IsInsideFunc isInside) {</unsigned>
|
|
Shinya Kitaoka |
120a6e |
assert(ras->getLx() == dtRas->getLx() && ras->getLy() == dtRas->getLy());
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
static const unsigned int uiMax = // Due to the above takeoverDist, for
|
|
Shinya Kitaoka |
120a6e |
(std::numeric_limits<unsigned int="">::max)() - 2; // d == 1</unsigned>
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
int lx = ras->getLx(), ly = ras->getLy();
|
|
Shinya Kitaoka |
120a6e |
for (int y = 0; y != ly; ++y) {
|
|
Shinya Kitaoka |
120a6e |
Pix *pix = ras->pixels(y), *rowEnd = pix + lx;
|
|
Shinya Kitaoka |
120a6e |
unsigned int *dt = dtRas->pixels(y);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (; pix != rowEnd; ++pix, ++dt) {
|
|
Shinya Kitaoka |
120a6e |
assert(*dt == 0u);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
if (!isInside(*pix)) *dt = uiMax;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename outfunc="" pix,="" typename=""></typename>
|
|
Shinya Kitaoka |
120a6e |
void expand(int lineLength, int linesCount, Pix *buf, int incrPix, int incrLine,
|
|
Shinya Kitaoka |
120a6e |
unsigned int *dtBuf, int dtIncrPix, int dtIncrLine,
|
|
Shinya Kitaoka |
120a6e |
OutFunc outFunc) {
|
|
Shinya Kitaoka |
120a6e |
struct locals {
|
|
Shinya Kitaoka |
120a6e |
static void copyLine(unsigned int *dst, unsigned int *src,
|
|
Shinya Kitaoka |
120a6e |
unsigned int *srcEnd, int srcStride) {
|
|
Shinya Kitaoka |
120a6e |
for (; src != srcEnd; src += srcStride, ++dst) *dst = *src;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
static void buildRange(unsigned int *dtRef, unsigned int *dtLineEnd,
|
|
Shinya Kitaoka |
120a6e |
unsigned int *&dtEnd, unsigned int *&dtNewRef) {
|
|
Shinya Kitaoka |
120a6e |
unsigned int d = 1,
|
|
Shinya Kitaoka |
120a6e |
dNew = 0, // dNew at 0 to provide a consistent dtNewRef
|
|
Shinya Kitaoka |
120a6e |
dMax = (std::numeric_limits<unsigned int="">::max)(); // at the end -</unsigned>
|
|
Shinya Kitaoka |
120a6e |
// should not
|
|
Shinya Kitaoka |
120a6e |
// matter though
|
|
Shinya Kitaoka |
120a6e |
unsigned int *dt = dtRef + 1;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
for (; d <= dMax && dt != dtLineEnd;
|
|
Shinya Kitaoka |
120a6e |
++d, ++dt) // Pick larger intervals if possible
|
|
Shinya Kitaoka |
120a6e |
{
|
|
Shinya Kitaoka |
120a6e |
unsigned int newDMax = ::takeoverDist(*dtRef, *dt, d); //
|
|
Shinya Kitaoka |
120a6e |
if (newDMax <= dMax) {
|
|
Shinya Kitaoka |
120a6e |
dNew = d;
|
|
Shinya Kitaoka |
120a6e |
dMax = newDMax;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
dtEnd =
|
|
Shinya Kitaoka |
120a6e |
dtRef + std::min(d, dMax); // Could end the line before (dMax < d)
|
|
Shinya Kitaoka |
120a6e |
dtNewRef = dtRef + dNew;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}; // locals
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Allocate a buffer equivalent to a dt line. It will store the original
|
|
Shinya Kitaoka |
120a6e |
// dt values. Final dt values will be written directly on the dt raster.
|
|
Shinya Kitaoka |
120a6e |
// This is necessary since read and write intervals overlap.
|
|
Shinya Kitaoka |
120a6e |
std::unique_ptr<unsigned[]> dtOriginalLine(new unsigned[lineLength]);</unsigned[]>
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
unsigned int *odtLineStart = dtOriginalLine.get(),
|
|
Shinya Kitaoka |
120a6e |
*odtLineEnd = odtLineStart + lineLength;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Process each line
|
|
Shinya Kitaoka |
120a6e |
for (int l = 0; l != linesCount; ++l) {
|
|
Shinya Kitaoka |
120a6e |
unsigned int *dtLineStart =
|
|
Shinya Kitaoka |
120a6e |
dtBuf +
|
|
Shinya Kitaoka |
120a6e |
dtIncrLine *
|
|
Shinya Kitaoka |
120a6e |
l, // Using dtBuf to track colors from now on,
|
|
Shinya Kitaoka |
120a6e |
*dtLineEnd =
|
|
Shinya Kitaoka |
120a6e |
dtLineStart +
|
|
Shinya Kitaoka |
120a6e |
dtIncrPix * lineLength, // it already embeds colorFunc's output due
|
|
Shinya Kitaoka |
120a6e |
*dt = dtLineStart, // to the way it was initialized.
|
|
Shinya Kitaoka |
120a6e |
*odtRef = odtLineStart;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
Pix *lineStart = buf + incrLine * l, *pix = lineStart;
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Make a copy of the original dt values
|
|
Shinya Kitaoka |
120a6e |
locals::copyLine(dtOriginalLine.get(), dtLineStart, dtLineEnd, dtIncrPix);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Expand a colored pixel along the line
|
|
Shinya Kitaoka |
120a6e |
while (dt != dtLineEnd) {
|
|
Shinya Kitaoka |
120a6e |
// The line is subdivided in consecutive ranges associated to the same
|
|
Shinya Kitaoka |
120a6e |
// half-parabola - process one
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Build a half-parabola range
|
|
Shinya Kitaoka |
120a6e |
unsigned int *dtEnd, *odtNewRef;
|
|
Shinya Kitaoka |
120a6e |
locals::buildRange(odtRef, odtLineEnd, dtEnd, odtNewRef);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
assert(odtLineStart <= odtNewRef && odtNewRef <= odtLineEnd);
|
|
Shinya Kitaoka |
120a6e |
assert(odtLineStart <= dtEnd && dtEnd <= odtLineEnd);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
dtEnd =
|
|
Shinya Kitaoka |
120a6e |
dtLineStart +
|
|
Shinya Kitaoka |
120a6e |
dtIncrPix *
|
|
Shinya Kitaoka |
120a6e |
(dtEnd - odtLineStart); // Convert dtEnd to the dt raster buffer
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Process the range
|
|
Shinya Kitaoka |
120a6e |
Pix *ref = lineStart + incrPix * (odtRef - odtLineStart);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
unsigned int d = (pix - ref) / incrPix;
|
|
Shinya Kitaoka |
120a6e |
for (; dt != dtEnd; ++d, dt += dtIncrPix, pix += incrPix)
|
|
Shinya Kitaoka |
120a6e |
outFunc(*pix, *ref, *dt = *odtRef + sq(d));
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
odtRef = odtNewRef;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//--------------------------------------------------------------
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*!
|
|
Toshihiro Shimizu |
890ddd |
\brief Performs an O(rows * cols) distance transform on the specified
|
|
Toshihiro Shimizu |
890ddd |
raster image.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\details The algorithm relies on the separability of the 2D DT into 2
|
|
Toshihiro Shimizu |
890ddd |
passes (by rows and columns) of 1-dimensional DTs.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
The 1D DT sums a pre-existing (from the previous DT step if any)
|
|
Toshihiro Shimizu |
890ddd |
DT result with the one currently calculated.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\warning Templace parameter OutFunc is supposed to satisfy \a transitivity
|
|
Toshihiro Shimizu |
890ddd |
upon comparison of its output - so, if \p b is the output of \p a,
|
|
Toshihiro Shimizu |
890ddd |
and \p c is the output of \p b, then \p c is the same as the output
|
|
Toshihiro Shimizu |
890ddd |
of \p a.
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
\todo Accept a different output raster - but preserve the case where
|
|
Toshihiro Shimizu |
890ddd |
(srcRas == dstRas).
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
template <typename isinsidefunc,="" outfunc="" pix,="" typename=""></typename>
|
|
Shinya Kitaoka |
120a6e |
void distanceTransform(const TRasterPT<pix> &ras, IsInsideFunc isInside,</pix>
|
|
Shinya Kitaoka |
120a6e |
OutFunc outFunc) {
|
|
Shinya Kitaoka |
120a6e |
int lx = ras->getLx(), ly = ras->getLy();
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
// Allocate a suitable temporary raster holding the (squared) distance
|
|
Shinya Kitaoka |
120a6e |
// transform
|
|
Shinya Kitaoka |
120a6e |
// built from the specified color function
|
|
Shinya Kitaoka |
120a6e |
TRasterPT<unsigned int=""> dtRas(</unsigned>
|
|
Shinya Kitaoka |
120a6e |
lx, ly); // Summed squared distances will be limited to
|
|
Shinya Kitaoka |
120a6e |
// 2 billions. This is generally suitable.
|
|
Shinya Kitaoka |
120a6e |
::initializeDT(ras, dtRas,
|
|
Shinya Kitaoka |
120a6e |
isInside); // The raster is binarized directly into the
|
|
Shinya Kitaoka |
120a6e |
// auxiliary dtRas. Pixels in the set to expand
|
|
Shinya Kitaoka |
120a6e |
// will have value 0, the others a suitable high value.
|
|
Shinya Kitaoka |
120a6e |
expand(lx, ly, ras->pixels(0), 1, ras->getWrap(), dtRas->pixels(0), 1,
|
|
Shinya Kitaoka |
120a6e |
dtRas->getWrap(), outFunc);
|
|
Shinya Kitaoka |
120a6e |
expand(lx, ly, ras->pixels(0) + lx - 1, -1, ras->getWrap(),
|
|
Shinya Kitaoka |
120a6e |
dtRas->pixels(0) + lx - 1, -1, dtRas->getWrap(), outFunc);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
expand(ly, lx, ras->pixels(0), ras->getWrap(), 1, dtRas->pixels(0),
|
|
Shinya Kitaoka |
120a6e |
dtRas->getWrap(), 1, outFunc);
|
|
Shinya Kitaoka |
120a6e |
expand(ly, lx, ras->pixels(ly - 1), -ras->getWrap(), 1, dtRas->pixels(ly - 1),
|
|
Shinya Kitaoka |
120a6e |
-dtRas->getWrap(), 1, outFunc);
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Local Functors
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
/*
|
|
Toshihiro Shimizu |
890ddd |
Using functors here just to be absolutely sure that calls are not
|
|
Toshihiro Shimizu |
890ddd |
callbacks.
|
|
Toshihiro Shimizu |
890ddd |
*/
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
namespace {
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
struct SomePaint {
|
|
Shinya Kitaoka |
120a6e |
inline bool operator()(const TPixelCM32 &pix) const {
|
|
Shinya Kitaoka |
120a6e |
return (pix.getTone() != 0) || (pix.getPaint() != 0);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
struct CopyPaint {
|
|
Shinya Kitaoka |
120a6e |
inline void operator()(TPixelCM32 &out, const TPixelCM32 &in,
|
|
Shinya Kitaoka |
120a6e |
unsigned int) const {
|
|
Shinya Kitaoka |
120a6e |
out.setPaint(in.getPaint());
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// API functions
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void TRop::expandPaint(const TRasterCM32P &rasCM) {
|
|
Shinya Kitaoka |
120a6e |
distanceTransform(rasCM, SomePaint(), CopyPaint());
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
// Unit testing
|
|
Toshihiro Shimizu |
890ddd |
//************************************************************************
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
#if defined UNIT_TEST && !defined NDEBUG
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
namespace {
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
void assertEqualBufs(const TRasterT<unsigned int=""> &a,</unsigned>
|
|
Shinya Kitaoka |
120a6e |
const TRasterT<unsigned int=""> &b) {</unsigned>
|
|
Shinya Kitaoka |
120a6e |
for (int y = 0; y != a.getLy(); ++y) {
|
|
Shinya Kitaoka |
120a6e |
for (int x = 0; x != a.getLx(); ++x)
|
|
Shinya Kitaoka |
120a6e |
assert(a.pixels(y)[x] == b.pixels(y)[x]);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
}
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
struct Selector {
|
|
Shinya Kitaoka |
120a6e |
inline bool operator()(unsigned int val) const { return val; }
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
struct OutputDT {
|
|
Shinya Kitaoka |
120a6e |
inline void operator()(unsigned int &out, const unsigned int &in,
|
|
Shinya Kitaoka |
120a6e |
unsigned int d2) const {
|
|
Shinya Kitaoka |
120a6e |
out = d2;
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
};
|
|
Toshihiro Shimizu |
890ddd |
|
|
Toshihiro Shimizu |
890ddd |
struct DTTest {
|
|
Shinya Kitaoka |
120a6e |
DTTest() {
|
|
Shinya Kitaoka |
120a6e |
unsigned int imgBuf[] = {
|
|
Shinya Kitaoka |
120a6e |
0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
|
|
Shinya Kitaoka |
120a6e |
1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
Shinya Kitaoka |
120a6e |
};
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
unsigned int dtBuf[] = {
|
|
Shinya Kitaoka |
120a6e |
4, 1, 0, 1, 4, 5, 2, 1, 0, 1, 1, 2, 1, 0, 0,
|
|
Shinya Kitaoka |
120a6e |
0, 0, 1, 1, 0, 1, 1, 1, 2, 2, 1, 2, 4, 4, 5,
|
|
Shinya Kitaoka |
120a6e |
};
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
TRasterPT<unsigned int=""> imgRas(6, 5, 6, imgBuf, false),</unsigned>
|
|
Shinya Kitaoka |
120a6e |
dtRas(6, 5, 6, dtBuf, false);
|
|
Shinya Kitaoka |
120a6e |
|
|
Shinya Kitaoka |
120a6e |
distanceTransform(imgRas, Selector(), OutputDT());
|
|
Shinya Kitaoka |
120a6e |
assertEqualBufs(*imgRas, *dtRas);
|
|
Shinya Kitaoka |
120a6e |
}
|
|
Toshihiro Shimizu |
890ddd |
} dtTest;
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
} // namespace
|
|
Toshihiro Shimizu |
890ddd |
|
|
Shinya Kitaoka |
120a6e |
#endif // UNIT_TEST && !NDEBUG
|