285 lines
9.6 KiB
C++
285 lines
9.6 KiB
C++
|
|
||
|
#define _CRT_SECURE_NO_WARNINGS
|
||
|
#include "shape-description.h"
|
||
|
|
||
|
namespace msdfgen {
|
||
|
|
||
|
int readCharF(FILE *input) {
|
||
|
int c = '\0';
|
||
|
do {
|
||
|
c = fgetc(input);
|
||
|
} while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
int readCharS(const char **input) {
|
||
|
int c = '\0';
|
||
|
do {
|
||
|
c = *(*input)++;
|
||
|
} while (c == ' ' || c == '\t' || c == '\r' || c == '\n');
|
||
|
if (!c) {
|
||
|
--c;
|
||
|
return EOF;
|
||
|
}
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
int readCoordF(FILE *input, Point2 &coord) {
|
||
|
return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
|
||
|
}
|
||
|
|
||
|
int readCoordS(const char **input, Point2 &coord) {
|
||
|
int read = 0;
|
||
|
int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
|
||
|
*input += read;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static bool writeCoord(FILE *output, Point2 coord) {
|
||
|
fprintf(output, "%.12g, %.12g", coord.x, coord.y);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
|
||
|
static int readControlPoints(T *input, Point2 *output) {
|
||
|
int result = readCoord(input, output[0]);
|
||
|
if (result == 2) {
|
||
|
switch (readChar(input)) {
|
||
|
case ')':
|
||
|
return 1;
|
||
|
case ';':
|
||
|
break;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
result = readCoord(input, output[1]);
|
||
|
if (result == 2 && readChar(input) == ')')
|
||
|
return 2;
|
||
|
} else if (result != 1 && readChar(input) == ')')
|
||
|
return 0;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)>
|
||
|
static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &colorsSpecified) {
|
||
|
Point2 p[4], start;
|
||
|
if (first)
|
||
|
p[0] = *first;
|
||
|
else {
|
||
|
int result = readCoord(input, p[0]);
|
||
|
if (result != 2)
|
||
|
return result != 1 && readChar(input) == terminator;
|
||
|
}
|
||
|
start = p[0];
|
||
|
int c = '\0';
|
||
|
while ((c = readChar(input)) != terminator) {
|
||
|
if (c != ';')
|
||
|
return false;
|
||
|
EdgeColor color = WHITE;
|
||
|
int result = readCoord(input, p[1]);
|
||
|
if (result == 2) {
|
||
|
output.addEdge(EdgeHolder(p[0], p[1], color));
|
||
|
p[0] = p[1];
|
||
|
continue;
|
||
|
} else if (result == 1)
|
||
|
return false;
|
||
|
else {
|
||
|
int controlPoints = 0;
|
||
|
switch ((c = readChar(input))) {
|
||
|
case '#':
|
||
|
output.addEdge(EdgeHolder(p[0], start, color));
|
||
|
p[0] = start;
|
||
|
continue;
|
||
|
case ';':
|
||
|
goto FINISH_EDGE;
|
||
|
case '(':
|
||
|
goto READ_CONTROL_POINTS;
|
||
|
case 'C': case 'c':
|
||
|
color = CYAN;
|
||
|
colorsSpecified = true;
|
||
|
break;
|
||
|
case 'M': case 'm':
|
||
|
color = MAGENTA;
|
||
|
colorsSpecified = true;
|
||
|
break;
|
||
|
case 'Y': case 'y':
|
||
|
color = YELLOW;
|
||
|
colorsSpecified = true;
|
||
|
break;
|
||
|
case 'W': case 'w':
|
||
|
color = WHITE;
|
||
|
colorsSpecified = true;
|
||
|
break;
|
||
|
default:
|
||
|
return c == terminator;
|
||
|
}
|
||
|
switch (readChar(input)) {
|
||
|
case ';':
|
||
|
goto FINISH_EDGE;
|
||
|
case '(':
|
||
|
READ_CONTROL_POINTS:
|
||
|
if ((controlPoints = readControlPoints<T, readChar, readCoord>(input, p+1)) < 0)
|
||
|
return false;
|
||
|
break;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
if (readChar(input) != ';')
|
||
|
return false;
|
||
|
FINISH_EDGE:
|
||
|
result = readCoord(input, p[1+controlPoints]);
|
||
|
if (result != 2) {
|
||
|
if (result == 1)
|
||
|
return false;
|
||
|
else {
|
||
|
if (readChar(input) == '#')
|
||
|
p[1+controlPoints] = start;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
switch (controlPoints) {
|
||
|
case 0:
|
||
|
output.addEdge(EdgeHolder(p[0], p[1], color));
|
||
|
p[0] = p[1];
|
||
|
continue;
|
||
|
case 1:
|
||
|
output.addEdge(EdgeHolder(p[0], p[1], p[2], color));
|
||
|
p[0] = p[2];
|
||
|
continue;
|
||
|
case 2:
|
||
|
output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color));
|
||
|
p[0] = p[3];
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool readShapeDescription(FILE *input, Shape &output, bool *colorsSpecified) {
|
||
|
bool locColorsSpec = false;
|
||
|
output.contours.clear();
|
||
|
output.inverseYAxis = false;
|
||
|
Point2 p;
|
||
|
int result = readCoordF(input, p);
|
||
|
if (result == 2) {
|
||
|
return readContour<FILE, readCharF, readCoordF>(input, output.addContour(), &p, EOF, locColorsSpec);
|
||
|
} else if (result == 1)
|
||
|
return false;
|
||
|
else {
|
||
|
int c = readCharF(input);
|
||
|
if (c == '@') {
|
||
|
char after = '\0';
|
||
|
if (fscanf(input, "invert-y%c", &after) != 1)
|
||
|
return feof(input) != 0;
|
||
|
output.inverseYAxis = true;
|
||
|
c = after;
|
||
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
||
|
c = readCharF(input);
|
||
|
}
|
||
|
for (; c == '{'; c = readCharF(input))
|
||
|
if (!readContour<FILE, readCharF, readCoordF>(input, output.addContour(), NULL, '}', locColorsSpec))
|
||
|
return false;
|
||
|
if (colorsSpecified)
|
||
|
*colorsSpecified = locColorsSpec;
|
||
|
return c == EOF && feof(input);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool readShapeDescription(const char *input, Shape &output, bool *colorsSpecified) {
|
||
|
bool locColorsSpec = false;
|
||
|
output.contours.clear();
|
||
|
output.inverseYAxis = false;
|
||
|
Point2 p;
|
||
|
int result = readCoordS(&input, p);
|
||
|
if (result == 2) {
|
||
|
return readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), &p, EOF, locColorsSpec);
|
||
|
} else if (result == 1)
|
||
|
return false;
|
||
|
else {
|
||
|
int c = readCharS(&input);
|
||
|
if (c == '@') {
|
||
|
for (int i = 0; i < (int) sizeof("invert-y")-1; ++i)
|
||
|
if (input[i] != "invert-y"[i])
|
||
|
return false;
|
||
|
output.inverseYAxis = true;
|
||
|
input += sizeof("invert-y")-1;
|
||
|
c = readCharS(&input);
|
||
|
}
|
||
|
for (; c == '{'; c = readCharS(&input))
|
||
|
if (!readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), NULL, '}', locColorsSpec))
|
||
|
return false;
|
||
|
if (colorsSpecified)
|
||
|
*colorsSpecified = locColorsSpec;
|
||
|
return c == EOF;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool isColored(const Shape &shape) {
|
||
|
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||
|
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge)
|
||
|
if ((*edge)->color != WHITE)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool writeShapeDescription(FILE *output, const Shape &shape) {
|
||
|
if (!shape.validate())
|
||
|
return false;
|
||
|
bool writeColors = isColored(shape);
|
||
|
if (shape.inverseYAxis)
|
||
|
fprintf(output, "@invert-y\n");
|
||
|
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||
|
fprintf(output, "{\n");
|
||
|
if (!contour->edges.empty()) {
|
||
|
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||
|
char colorCode = '\0';
|
||
|
if (writeColors) {
|
||
|
switch ((*edge)->color) {
|
||
|
case YELLOW: colorCode = 'y'; break;
|
||
|
case MAGENTA: colorCode = 'm'; break;
|
||
|
case CYAN: colorCode = 'c'; break;
|
||
|
case WHITE: colorCode = 'w'; break;
|
||
|
default:;
|
||
|
}
|
||
|
}
|
||
|
if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) {
|
||
|
fprintf(output, "\t");
|
||
|
writeCoord(output, e->p[0]);
|
||
|
fprintf(output, ";\n");
|
||
|
if (colorCode)
|
||
|
fprintf(output, "\t\t%c;\n", colorCode);
|
||
|
}
|
||
|
if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) {
|
||
|
fprintf(output, "\t");
|
||
|
writeCoord(output, e->p[0]);
|
||
|
fprintf(output, ";\n\t\t");
|
||
|
if (colorCode)
|
||
|
fprintf(output, "%c", colorCode);
|
||
|
fprintf(output, "(");
|
||
|
writeCoord(output, e->p[1]);
|
||
|
fprintf(output, ");\n");
|
||
|
}
|
||
|
if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) {
|
||
|
fprintf(output, "\t");
|
||
|
writeCoord(output, e->p[0]);
|
||
|
fprintf(output, ";\n\t\t");
|
||
|
if (colorCode)
|
||
|
fprintf(output, "%c", colorCode);
|
||
|
fprintf(output, "(");
|
||
|
writeCoord(output, e->p[1]);
|
||
|
fprintf(output, "; ");
|
||
|
writeCoord(output, e->p[2]);
|
||
|
fprintf(output, ");\n");
|
||
|
}
|
||
|
}
|
||
|
fprintf(output, "\t#\n");
|
||
|
}
|
||
|
fprintf(output, "}\n");
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
}
|