diff --git a/LICENSE.txt b/LICENSE.txt index 69054bd..70e932c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ 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 of this software and associated documentation files (the "Software"), to deal diff --git a/core/contour-combiners.cpp b/core/contour-combiners.cpp index bac5534..d97959c 100644 --- a/core/contour-combiners.cpp +++ b/core/contour-combiners.cpp @@ -16,6 +16,13 @@ static void initDistance(MultiDistance &distance) { 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) { return distance; } diff --git a/core/edge-coloring.cpp b/core/edge-coloring.cpp index da9b037..6cd9e47 100644 --- a/core/edge-coloring.cpp +++ b/core/edge-coloring.cpp @@ -11,6 +11,15 @@ 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) { return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold; } @@ -26,30 +35,45 @@ static double estimateEdgeLength(const EdgeSegment *edge) { return len; } -static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor banned = BLACK) { - EdgeColor combined = EdgeColor(color&banned); - 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); +static int seedExtract2(unsigned long long &seed) { + int v = int(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) { double crossThreshold = sin(angleThreshold); + EdgeColor color = initColor(seed); std::vector corners; for (std::vector::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { - // Identify corners - corners.clear(); - if (!contour->edges.empty()) { + if (contour->edges.empty()) + continue; + { // Identify corners + corners.clear(); Vector2 prevDirection = contour->edges.back()->direction(1); int index = 0; for (std::vector::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 - if (corners.empty()) + if (corners.empty()) { + switchColor(color, seed); for (std::vector::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) - (*edge)->color = WHITE; + (*edge)->color = color; + } // "Teardrop" case else if (corners.size() == 1) { - EdgeColor colors[3] = { WHITE, WHITE }; - switchColor(colors[0], seed); - switchColor(colors[2] = colors[0], seed); + EdgeColor colors[3]; + switchColor(color, seed); + colors[0] = color; + colors[1] = WHITE; + switchColor(color, seed); + colors[2] = color; 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]; + contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)]; } else if (contour->edges.size() >= 1) { // Less than three edge segments for three colors => edges must be split EdgeSegment *parts[7] = { }; @@ -98,7 +127,6 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long int spline = 0; int start = corners[0]; int m = (int) contour->edges.size(); - EdgeColor color = WHITE; switchColor(color, seed); EdgeColor initialColor = color; for (int i = 0; i < m; ++i) { @@ -123,12 +151,14 @@ struct EdgeColoringInkTrapCorner { void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) { typedef EdgeColoringInkTrapCorner Corner; double crossThreshold = sin(angleThreshold); + EdgeColor color = initColor(seed); std::vector corners; for (std::vector::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { - // Identify corners + if (contour->edges.empty()) + continue; double splineLength = 0; - corners.clear(); - if (!contour->edges.empty()) { + { // Identify corners + corners.clear(); Vector2 prevDirection = contour->edges.back()->direction(1); int index = 0; for (std::vector::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 - if (corners.empty()) + if (corners.empty()) { + switchColor(color, seed); for (std::vector::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) - (*edge)->color = WHITE; + (*edge)->color = color; + } // "Teardrop" case else if (corners.size() == 1) { - EdgeColor colors[3] = { WHITE, WHITE }; - switchColor(colors[0], seed); - switchColor(colors[2] = colors[0], seed); + EdgeColor colors[3]; + switchColor(color, seed); + colors[0] = color; + colors[1] = WHITE; + switchColor(color, seed); + colors[2] = color; int corner = corners[0].index; 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]; + contour->edges[(corner+i)%m]->color = colors[1+symmetricalTrichotomy(i, m)]; } else if (contour->edges.size() >= 1) { // Less than three edge segments for three colors => edges must be split EdgeSegment *parts[7] = { }; @@ -191,7 +226,6 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long } } } - EdgeColor color = WHITE; EdgeColor initialColor = BLACK; for (int i = 0; i < cornerCount; ++i) { if (!corners[i].minor) { @@ -271,23 +305,19 @@ static void colorSecondDegreeGraph(int *coloring, const int *const *edgeMatrix, color = 1; break; case 3: - color = (int) seed&1; - seed >>= 1; + color = seedExtract2(seed); // 0 or 1 break; case 4: color = 2; break; case 5: - color = ((int) seed+1&1)<<1; - seed >>= 1; + color = (int) !seedExtract2(seed)<<1; // 2 or 0 break; case 6: - color = ((int) seed&1)+1; - seed >>= 1; + color = seedExtract2(seed)+1; // 1 or 2 break; case 7: - color = int((seed+i)%3); - seed /= 3; + color = (seedExtract3(seed)+i)%3; // 0 or 1 or 2 break; } coloring[i] = color; @@ -394,7 +424,7 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l for (int i = 0; i < m; ++i) { if (i == m/2) 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]); else contour->edges[(corner+i)%m]->color = WHITE; diff --git a/main.cpp b/main.cpp index 5253a44..5645a47 100644 --- a/main.cpp +++ b/main.cpp @@ -2,7 +2,7 @@ /* * 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 * */ diff --git a/msdfgen-ext.h b/msdfgen-ext.h index 791a950..cf9d3aa 100644 --- a/msdfgen-ext.h +++ b/msdfgen-ext.h @@ -4,7 +4,7 @@ /* * 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. * diff --git a/msdfgen.h b/msdfgen.h index dff8beb..8a66039 100644 --- a/msdfgen.h +++ b/msdfgen.h @@ -4,7 +4,7 @@ /* * 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 * has been developed by Viktor Chlumsky in 2014 for his master's thesis,