Experimental version of 7TSDF mode

This commit is contained in:
Chlumsky 2024-02-24 23:17:58 +01:00
parent 682381a03c
commit 7f6a8aebba
8 changed files with 373 additions and 0 deletions

View File

@ -16,6 +16,17 @@ static void initDistance(MultiDistance &distance) {
distance.b = -DBL_MAX;
}
static void initDistance(M7AndTrueDistanceSelector::DistanceType &distance) {
distance.p[0] = -DBL_MAX;
distance.p[1] = -DBL_MAX;
distance.p[2] = -DBL_MAX;
distance.p[3] = -DBL_MAX;
distance.p[4] = -DBL_MAX;
distance.p[5] = -DBL_MAX;
distance.p[6] = -DBL_MAX;
distance.t = -DBL_MAX;
}
static double resolveDistance(double distance) {
return distance;
}
@ -24,6 +35,15 @@ static double resolveDistance(const MultiDistance &distance) {
return median(distance.r, distance.g, distance.b);
}
static int cmpDbl(const void *a, const void *b) {
return *reinterpret_cast<const double *>(a) < *reinterpret_cast<const double *>(b);
}
static double resolveDistance(M7AndTrueDistanceSelector::DistanceType distance) {
qsort(distance.p, 7, sizeof(*distance.p), &cmpDbl);
return distance.p[3];
}
template <class EdgeSelector>
SimpleContourCombiner<EdgeSelector>::SimpleContourCombiner(const Shape &shape) { }
@ -46,6 +66,7 @@ template class SimpleContourCombiner<TrueDistanceSelector>;
template class SimpleContourCombiner<PseudoDistanceSelector>;
template class SimpleContourCombiner<MultiDistanceSelector>;
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
template class SimpleContourCombiner<M7AndTrueDistanceSelector>;
template <class EdgeSelector>
OverlappingContourCombiner<EdgeSelector>::OverlappingContourCombiner(const Shape &shape) {
@ -130,5 +151,6 @@ template class OverlappingContourCombiner<TrueDistanceSelector>;
template class OverlappingContourCombiner<PseudoDistanceSelector>;
template class OverlappingContourCombiner<MultiDistanceSelector>;
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;
template class OverlappingContourCombiner<M7AndTrueDistanceSelector>;
}

View File

@ -498,4 +498,140 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
}
}
void edgeColoring7Random(Shape &shape, double angleThreshold, unsigned long long seed) {
/*int colorPool[] = {
0b00001111,
0b00010111,
0b00011011,
0b00011101,
0b00011110,
0b00100111,
0b00101011,
0b00101101,
0b00101110,
0b00110011,
0b00110101,
0b00110110,
0b00111001,
0b00111010,
0b00111100,
0b01000111,
0b01001011,
0b01001101,
0b01001110,
0b01010011,
0b01010101,
0b01010110,
0b01011001,
0b01011010,
0b01011100,
0b01100011,
0b01100101,
0b01100110,
0b01101001,
0b01101010,
0b01101100,
0b01110001,
0b01110010,
0b01110100,
0b01111000,
};
static_assert(sizeof(colorPool) == 35*sizeof(*colorPool), "Expected 35 colors in color pool");*/
int colorPool[] = {
// All pairs share exactly 2 set bits
0b00001111,
0b00111100,
0b00110011,
0b01010101,
0b01011010,
0b01100110,
0b01101001,
};
int colorI = 0;
#define NEXT_COLOR() EdgeColor(colorPool[colorI++%(int(sizeof(colorPool)/sizeof(*colorPool)))])
double crossThreshold = sin(angleThreshold);
std::vector<int> corners;
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
if (seed) {
// Shuffle colorPool
for (int i = int(sizeof(colorPool)/sizeof(*colorPool))-1; i; --i) {
seed = 6364136223846793005ll*seed+1442695040888963407ll; // LCG
int j = int(seed%(i+1));
int tmp = colorPool[i];
colorPool[i] = colorPool[j];
colorPool[j] = tmp;
}
}
// Identify corners
corners.clear();
if (!contour->edges.empty()) {
Vector2 prevDirection = contour->edges.back()->direction(1);
int index = 0;
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
if (isCorner(prevDirection.normalize(), (*edge)->direction(0).normalize(), crossThreshold))
corners.push_back(index);
prevDirection = (*edge)->direction(1);
}
}
// Smooth contour
if (corners.empty()) {
EdgeColor color = NEXT_COLOR();
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
(*edge)->color = color;
}
// "Teardrop" case
else if (corners.size() == 1) {
EdgeColor colors[3];
colors[0] = NEXT_COLOR();
colors[2] = NEXT_COLOR();
colors[1] = EdgeColor(colors[0]|colors[2]);
int corner = corners[0];
if (contour->edges.size() >= 3) {
int m = (int) contour->edges.size();
for (int i = 0; i < m; ++i)
contour->edges[(corner+i)%m]->color = (colors+1)[int(3+2.875*i/(m-1)-1.4375+.5)-3];
} else if (contour->edges.size() >= 1) {
// Less than three edge segments for three colors => edges must be split
EdgeSegment *parts[7] = { };
contour->edges[0]->splitInThirds(parts[0+3*corner], parts[1+3*corner], parts[2+3*corner]);
if (contour->edges.size() >= 2) {
contour->edges[1]->splitInThirds(parts[3-3*corner], parts[4-3*corner], parts[5-3*corner]);
parts[0]->color = parts[1]->color = colors[0];
parts[2]->color = parts[3]->color = colors[1];
parts[4]->color = parts[5]->color = colors[2];
} else {
parts[0]->color = colors[0];
parts[1]->color = colors[1];
parts[2]->color = colors[2];
}
contour->edges.clear();
for (int i = 0; parts[i]; ++i)
contour->edges.push_back(EdgeHolder(parts[i]));
}
}
// Multiple corners
else {
int cornerCount = (int) corners.size();
int spline = 0;
int start = corners[0];
int m = (int) contour->edges.size();
EdgeColor color = NEXT_COLOR();
EdgeColor initialColor = color;
for (int i = 0; i < m; ++i) {
int index = (start+i)%m;
if (spline+1 < cornerCount && corners[spline+1] == index) {
++spline;
color = NEXT_COLOR();
if (spline == cornerCount-1 && color == initialColor)
color = NEXT_COLOR();
}
contour->edges[index]->color = color;
}
}
}
}
}

View File

@ -26,4 +26,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
*/
void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed = 0);
void edgeColoring7Random(Shape &shape, double angleThreshold, unsigned long long seed = 0);
}

View File

@ -258,4 +258,139 @@ MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distanc
return mtd;
}
void M7AndTrueDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
b[0].reset(delta);
b[1].reset(delta);
b[2].reset(delta);
b[3].reset(delta);
b[4].reset(delta);
b[5].reset(delta);
b[6].reset(delta);
this->p = p;
}
void M7AndTrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
if (
(edge->color&1<<0 && b[0].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<1 && b[1].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<2 && b[2].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<3 && b[3].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<4 && b[4].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<5 && b[5].isEdgeRelevant(cache, edge, p)) ||
(edge->color&1<<6 && b[6].isEdgeRelevant(cache, edge, p))
) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
if (edge->color&1<<0)
b[0].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<1)
b[1].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<2)
b[2].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<3)
b[3].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<4)
b[4].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<5)
b[5].addEdgeTrueDistance(edge, distance, param);
if (edge->color&1<<6)
b[6].addEdgeTrueDistance(edge, distance, param);
cache.point = p;
cache.absDistance = fabs(distance.distance);
Vector2 ap = p-edge->point(0);
Vector2 bp = p-edge->point(1);
Vector2 aDir = edge->direction(0).normalize(true);
Vector2 bDir = edge->direction(1).normalize(true);
Vector2 prevDir = prevEdge->direction(1).normalize(true);
Vector2 nextDir = nextEdge->direction(0).normalize(true);
double add = dotProduct(ap, (prevDir+aDir).normalize(true));
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
if (add > 0) {
double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
pd = -pd;
if (edge->color&1<<0)
b[0].addEdgePseudoDistance(pd);
if (edge->color&1<<1)
b[1].addEdgePseudoDistance(pd);
if (edge->color&1<<2)
b[2].addEdgePseudoDistance(pd);
if (edge->color&1<<3)
b[3].addEdgePseudoDistance(pd);
if (edge->color&1<<4)
b[4].addEdgePseudoDistance(pd);
if (edge->color&1<<5)
b[5].addEdgePseudoDistance(pd);
if (edge->color&1<<6)
b[6].addEdgePseudoDistance(pd);
}
cache.aPseudoDistance = pd;
}
if (bdd > 0) {
double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
if (edge->color&1<<0)
b[0].addEdgePseudoDistance(pd);
if (edge->color&1<<1)
b[1].addEdgePseudoDistance(pd);
if (edge->color&1<<2)
b[2].addEdgePseudoDistance(pd);
if (edge->color&1<<3)
b[3].addEdgePseudoDistance(pd);
if (edge->color&1<<4)
b[4].addEdgePseudoDistance(pd);
if (edge->color&1<<5)
b[5].addEdgePseudoDistance(pd);
if (edge->color&1<<6)
b[6].addEdgePseudoDistance(pd);
}
cache.bPseudoDistance = pd;
}
cache.aDomainDistance = add;
cache.bDomainDistance = bdd;
}
}
void M7AndTrueDistanceSelector::merge(const M7AndTrueDistanceSelector &other) {
b[0].merge(other.b[0]);
b[1].merge(other.b[1]);
b[2].merge(other.b[2]);
b[3].merge(other.b[3]);
b[4].merge(other.b[4]);
b[5].merge(other.b[5]);
b[6].merge(other.b[6]);
}
M7AndTrueDistanceSelector::DistanceType M7AndTrueDistanceSelector::distance() const {
DistanceType multiDistance;
multiDistance.p[0] = b[0].computeDistance(p);
multiDistance.p[1] = b[1].computeDistance(p);
multiDistance.p[2] = b[2].computeDistance(p);
multiDistance.p[3] = b[3].computeDistance(p);
multiDistance.p[4] = b[4].computeDistance(p);
multiDistance.p[5] = b[5].computeDistance(p);
multiDistance.p[6] = b[6].computeDistance(p);
multiDistance.t = trueDistance().distance;
return multiDistance;
}
SignedDistance M7AndTrueDistanceSelector::trueDistance() const {
SignedDistance distance = b[0].trueDistance();
if (b[1].trueDistance() < distance)
distance = b[1].trueDistance();
if (b[2].trueDistance() < distance)
distance = b[2].trueDistance();
if (b[3].trueDistance() < distance)
distance = b[3].trueDistance();
if (b[4].trueDistance() < distance)
distance = b[4].trueDistance();
if (b[5].trueDistance() < distance)
distance = b[5].trueDistance();
if (b[6].trueDistance() < distance)
distance = b[6].trueDistance();
return distance;
}
}

View File

@ -114,4 +114,25 @@ public:
};
class M7AndTrueDistanceSelector {
public:
typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
struct DistanceType {
double p[7];
double t;
};
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
void merge(const M7AndTrueDistanceSelector &other);
DistanceType distance() const;
SignedDistance trueDistance() const;
private:
Point2 p;
PseudoDistanceSelectorBase b[7];
};
}

View File

@ -49,6 +49,24 @@ public:
}
};
template <>
class DistancePixelConversion<M7AndTrueDistanceSelector::DistanceType> {
double invRange;
public:
typedef BitmapRef<float, 8> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
inline void operator()(float *pixels, const M7AndTrueDistanceSelector::DistanceType &distance) const {
pixels[0] = float(invRange*distance.p[0]+.5);
pixels[1] = float(invRange*distance.p[1]+.5);
pixels[2] = float(invRange*distance.p[2]+.5);
pixels[3] = float(invRange*distance.p[3]+.5);
pixels[4] = float(invRange*distance.p[4]+.5);
pixels[5] = float(invRange*distance.p[5]+.5);
pixels[6] = float(invRange*distance.p[6]+.5);
pixels[7] = float(invRange*distance.t+.5);
}
};
template <class ContourCombiner>
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
@ -104,6 +122,13 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
msdfErrorCorrection(output, shape, projection, range, config);
}
void generate7TSDF(const BitmapRef<float, 8> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<M7AndTrueDistanceSelector> >(output, shape, projection, range);
else
generateDistanceField<SimpleContourCombiner<M7AndTrueDistanceSelector> >(output, shape, projection, range);
}
// Legacy API
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {

View File

@ -516,6 +516,7 @@ int main(int argc, const char *const *argv) {
PSEUDO,
MULTI,
MULTI_AND_TRUE,
MULTI7_AND_TRUE,
METRICS
} mode = MULTI;
enum {
@ -592,6 +593,7 @@ int main(int argc, const char *const *argv) {
ARG_MODE("psdf", PSEUDO)
ARG_MODE("msdf", MULTI)
ARG_MODE("mtsdf", MULTI_AND_TRUE)
ARG_MODE("7tsdf", MULTI7_AND_TRUE)
ARG_MODE("metrics", METRICS)
#if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_SVG)
@ -1140,6 +1142,7 @@ int main(int argc, const char *const *argv) {
Bitmap<float, 1> sdf;
Bitmap<float, 3> msdf;
Bitmap<float, 4> mtsdf;
Bitmap<float, 8> m7tsdf;
MSDFGeneratorConfig postErrorCorrectionConfig(generatorConfig);
if (scanlinePass) {
if (explicitErrorCorrectionMode && generatorConfig.errorCorrection.distanceCheckMode != ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE) {
@ -1196,6 +1199,27 @@ int main(int argc, const char *const *argv) {
generateMTSDF(mtsdf, shape, projection, range, generatorConfig);
break;
}
case MULTI7_AND_TRUE:
edgeColoring7Random(shape, angleThreshold, coloringSeed);
m7tsdf = Bitmap<float, 8>(width, height);
generate7TSDF(m7tsdf, shape, projection, range, generatorConfig);
mtsdf = Bitmap<float, 4>(width, 2*height);
for (float
*dstLow = mtsdf(0, 0),
*dstHigh = mtsdf(0, height),
*src = m7tsdf(0, 0),
*srcEnd = m7tsdf(0, height);
src < srcEnd; dstLow += 4, dstHigh += 4, src += 8
) {
dstLow[0] = src[0];
dstLow[1] = src[1];
dstLow[2] = src[2];
dstLow[3] = src[3];
dstHigh[0] = src[4];
dstHigh[1] = src[5];
dstHigh[2] = src[6];
dstHigh[3] = src[7];
}
default:;
}
@ -1343,6 +1367,11 @@ int main(int argc, const char *const *argv) {
fputs("Failed to write test render file.\n", stderr);
}
break;
case MULTI7_AND_TRUE:
if ((error = writeOutput<4>(mtsdf, output, format))) {
fprintf(stderr, "%s\n", error);
return 1;
}
default:;
}

View File

@ -49,6 +49,9 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
/// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// Generates a 7+1 channel signed distance field with true distance in the last channel. Edge colors must be assigned first.
void generate7TSDF(const BitmapRef<float, 8> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
// Old version of the function API's kept for backwards compatibility
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);