mirror of https://github.com/Chlumsky/msdfgen.git
Edge coloring update, circles no longer white
This commit is contained in:
parent
682381a03c
commit
35f92541c4
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016 - 2023 Viktor Chlumsky
|
Copyright (c) 2016 - 2024 Viktor Chlumsky
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ static void initDistance(MultiDistance &distance) {
|
||||||
distance.b = -DBL_MAX;
|
distance.b = -DBL_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void initDistance(MultiAndTrueDistance &distance) {
|
||||||
|
distance.r = -DBL_MAX;
|
||||||
|
distance.g = -DBL_MAX;
|
||||||
|
distance.b = -DBL_MAX;
|
||||||
|
distance.a = -DBL_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
static double resolveDistance(double distance) {
|
static double resolveDistance(double distance) {
|
||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,15 @@
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each position < n, this function will return -1, 0, or 1,
|
||||||
|
* depending on whether the position is closer to the beginning, middle, or end, respectively.
|
||||||
|
* It is guaranteed that the output will be balanced in that the total for positions 0 through n-1 will be zero.
|
||||||
|
*/
|
||||||
|
static int symmetricalTrichotomy(int position, int n) {
|
||||||
|
return int(3+2.875*position/(n-1)-1.4375+.5)-3;
|
||||||
|
}
|
||||||
|
|
||||||
static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
|
static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
|
||||||
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
|
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
|
||||||
}
|
}
|
||||||
|
|
@ -26,30 +35,45 @@ static double estimateEdgeLength(const EdgeSegment *edge) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) {
|
static int seedExtract2(unsigned long long &seed) {
|
||||||
EdgeColor combined = EdgeColor(color&banned);
|
int v = int(seed)&1;
|
||||||
if (combined == RED || combined == GREEN || combined == BLUE) {
|
|
||||||
color = EdgeColor(combined^WHITE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (color == BLACK || color == WHITE) {
|
|
||||||
static const EdgeColor start[3] = { CYAN, MAGENTA, YELLOW };
|
|
||||||
color = start[seed%3];
|
|
||||||
seed /= 3;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int shifted = color<<(1+(seed&1));
|
|
||||||
color = EdgeColor((shifted|shifted>>3)&WHITE);
|
|
||||||
seed >>= 1;
|
seed >>= 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seedExtract3(unsigned long long &seed) {
|
||||||
|
int v = int(seed%3);
|
||||||
|
seed /= 3;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EdgeColor initColor(unsigned long long &seed) {
|
||||||
|
static const EdgeColor colors[3] = { CYAN, MAGENTA, YELLOW };
|
||||||
|
return colors[seedExtract3(seed)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void switchColor(EdgeColor &color, unsigned long long &seed) {
|
||||||
|
int shifted = color<<(1+seedExtract2(seed));
|
||||||
|
color = EdgeColor((shifted|shifted>>3)&WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned) {
|
||||||
|
EdgeColor combined = EdgeColor(color&banned);
|
||||||
|
if (combined == RED || combined == GREEN || combined == BLUE)
|
||||||
|
color = EdgeColor(combined^WHITE);
|
||||||
|
else
|
||||||
|
switchColor(color, seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
|
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||||
double crossThreshold = sin(angleThreshold);
|
double crossThreshold = sin(angleThreshold);
|
||||||
|
EdgeColor color = initColor(seed);
|
||||||
std::vector<int> corners;
|
std::vector<int> corners;
|
||||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||||||
// Identify corners
|
if (contour->edges.empty())
|
||||||
corners.clear();
|
continue;
|
||||||
if (!contour->edges.empty()) {
|
{ // Identify corners
|
||||||
|
corners.clear();
|
||||||
Vector2 prevDirection = contour->edges.back()->direction(1);
|
Vector2 prevDirection = contour->edges.back()->direction(1);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
|
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
|
||||||
|
|
@ -60,19 +84,24 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smooth contour
|
// Smooth contour
|
||||||
if (corners.empty())
|
if (corners.empty()) {
|
||||||
|
switchColor(color, seed);
|
||||||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
||||||
(*edge)->color = WHITE;
|
(*edge)->color = color;
|
||||||
|
}
|
||||||
// "Teardrop" case
|
// "Teardrop" case
|
||||||
else if (corners.size() == 1) {
|
else if (corners.size() == 1) {
|
||||||
EdgeColor colors[3] = { WHITE, WHITE };
|
EdgeColor colors[3];
|
||||||
switchColor(colors[0], seed);
|
switchColor(color, seed);
|
||||||
switchColor(colors[2] = colors[0], seed);
|
colors[0] = color;
|
||||||
|
colors[1] = WHITE;
|
||||||
|
switchColor(color, seed);
|
||||||
|
colors[2] = color;
|
||||||
int corner = corners[0];
|
int corner = corners[0];
|
||||||
if (contour->edges.size() >= 3) {
|
if (contour->edges.size() >= 3) {
|
||||||
int m = (int) contour->edges.size();
|
int m = (int) contour->edges.size();
|
||||||
for (int i = 0; i < m; ++i)
|
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];
|
contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)];
|
||||||
} else if (contour->edges.size() >= 1) {
|
} else if (contour->edges.size() >= 1) {
|
||||||
// Less than three edge segments for three colors => edges must be split
|
// Less than three edge segments for three colors => edges must be split
|
||||||
EdgeSegment *parts[7] = { };
|
EdgeSegment *parts[7] = { };
|
||||||
|
|
@ -98,7 +127,6 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
|
||||||
int spline = 0;
|
int spline = 0;
|
||||||
int start = corners[0];
|
int start = corners[0];
|
||||||
int m = (int) contour->edges.size();
|
int m = (int) contour->edges.size();
|
||||||
EdgeColor color = WHITE;
|
|
||||||
switchColor(color, seed);
|
switchColor(color, seed);
|
||||||
EdgeColor initialColor = color;
|
EdgeColor initialColor = color;
|
||||||
for (int i = 0; i < m; ++i) {
|
for (int i = 0; i < m; ++i) {
|
||||||
|
|
@ -123,12 +151,14 @@ struct EdgeColoringInkTrapCorner {
|
||||||
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
|
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||||
typedef EdgeColoringInkTrapCorner Corner;
|
typedef EdgeColoringInkTrapCorner Corner;
|
||||||
double crossThreshold = sin(angleThreshold);
|
double crossThreshold = sin(angleThreshold);
|
||||||
|
EdgeColor color = initColor(seed);
|
||||||
std::vector<Corner> corners;
|
std::vector<Corner> corners;
|
||||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||||||
// Identify corners
|
if (contour->edges.empty())
|
||||||
|
continue;
|
||||||
double splineLength = 0;
|
double splineLength = 0;
|
||||||
corners.clear();
|
{ // Identify corners
|
||||||
if (!contour->edges.empty()) {
|
corners.clear();
|
||||||
Vector2 prevDirection = contour->edges.back()->direction(1);
|
Vector2 prevDirection = contour->edges.back()->direction(1);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
|
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge, ++index) {
|
||||||
|
|
@ -143,19 +173,24 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
|
||||||
}
|
}
|
||||||
|
|
||||||
// Smooth contour
|
// Smooth contour
|
||||||
if (corners.empty())
|
if (corners.empty()) {
|
||||||
|
switchColor(color, seed);
|
||||||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
||||||
(*edge)->color = WHITE;
|
(*edge)->color = color;
|
||||||
|
}
|
||||||
// "Teardrop" case
|
// "Teardrop" case
|
||||||
else if (corners.size() == 1) {
|
else if (corners.size() == 1) {
|
||||||
EdgeColor colors[3] = { WHITE, WHITE };
|
EdgeColor colors[3];
|
||||||
switchColor(colors[0], seed);
|
switchColor(color, seed);
|
||||||
switchColor(colors[2] = colors[0], seed);
|
colors[0] = color;
|
||||||
|
colors[1] = WHITE;
|
||||||
|
switchColor(color, seed);
|
||||||
|
colors[2] = color;
|
||||||
int corner = corners[0].index;
|
int corner = corners[0].index;
|
||||||
if (contour->edges.size() >= 3) {
|
if (contour->edges.size() >= 3) {
|
||||||
int m = (int) contour->edges.size();
|
int m = (int) contour->edges.size();
|
||||||
for (int i = 0; i < m; ++i)
|
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];
|
contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)];
|
||||||
} else if (contour->edges.size() >= 1) {
|
} else if (contour->edges.size() >= 1) {
|
||||||
// Less than three edge segments for three colors => edges must be split
|
// Less than three edge segments for three colors => edges must be split
|
||||||
EdgeSegment *parts[7] = { };
|
EdgeSegment *parts[7] = { };
|
||||||
|
|
@ -191,7 +226,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EdgeColor color = WHITE;
|
|
||||||
EdgeColor initialColor = BLACK;
|
EdgeColor initialColor = BLACK;
|
||||||
for (int i = 0; i < cornerCount; ++i) {
|
for (int i = 0; i < cornerCount; ++i) {
|
||||||
if (!corners[i].minor) {
|
if (!corners[i].minor) {
|
||||||
|
|
@ -271,23 +305,19 @@ static void colorSecondDegreeGraph(int *coloring, const int *const *edgeMatrix,
|
||||||
color = 1;
|
color = 1;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
color = (int) seed&1;
|
color = seedExtract2(seed); // 0 or 1
|
||||||
seed >>= 1;
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
color = 2;
|
color = 2;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
color = ((int) seed+1&1)<<1;
|
color = (int) !seedExtract2(seed)<<1; // 2 or 0
|
||||||
seed >>= 1;
|
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
color = ((int) seed&1)+1;
|
color = seedExtract2(seed)+1; // 1 or 2
|
||||||
seed >>= 1;
|
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
color = int((seed+i)%3);
|
color = (seedExtract3(seed)+i)%3; // 0 or 1 or 2
|
||||||
seed /= 3;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
coloring[i] = color;
|
coloring[i] = color;
|
||||||
|
|
@ -394,7 +424,7 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
|
||||||
for (int i = 0; i < m; ++i) {
|
for (int i = 0; i < m; ++i) {
|
||||||
if (i == m/2)
|
if (i == m/2)
|
||||||
splineStarts.push_back((int) edgeSegments.size());
|
splineStarts.push_back((int) edgeSegments.size());
|
||||||
if (int(3+2.875*i/(m-1)-1.4375+.5)-3)
|
if (symmetricalTrichotomy(i, m))
|
||||||
edgeSegments.push_back(&*contour->edges[(corner+i)%m]);
|
edgeSegments.push_back(&*contour->edges[(corner+i)%m]);
|
||||||
else
|
else
|
||||||
contour->edges[(corner+i)%m]->color = WHITE;
|
contour->edges[(corner+i)%m]->color = WHITE;
|
||||||
|
|
|
||||||
2
main.cpp
2
main.cpp
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR - standalone console program
|
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR - standalone console program
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------------
|
||||||
* A utility by Viktor Chlumsky, (c) 2014 - 2023
|
* A utility by Viktor Chlumsky, (c) 2014 - 2024
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/*
|
/*
|
||||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
|
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
|
||||||
* ---------------------------------------------
|
* ---------------------------------------------
|
||||||
* A utility by Viktor Chlumsky, (c) 2014 - 2023
|
* A utility by Viktor Chlumsky, (c) 2014 - 2024
|
||||||
*
|
*
|
||||||
* The extension module provides ways to easily load input and save output using popular formats.
|
* The extension module provides ways to easily load input and save output using popular formats.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/*
|
/*
|
||||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
|
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
|
||||||
* ---------------------------------------------
|
* ---------------------------------------------
|
||||||
* A utility by Viktor Chlumsky, (c) 2014 - 2023
|
* A utility by Viktor Chlumsky, (c) 2014 - 2024
|
||||||
*
|
*
|
||||||
* The technique used to generate multi-channel distance fields in this code
|
* The technique used to generate multi-channel distance fields in this code
|
||||||
* has been developed by Viktor Chlumsky in 2014 for his master's thesis,
|
* has been developed by Viktor Chlumsky in 2014 for his master's thesis,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue