Generalize the parsing of coordinates and geometry strings.
In preparation for future changes. New geometry and new coordinate syntax has been added as well: Additional node coordinate formats: <block>#[<node>] <block>.[<node>] Additional geometry formats: <x1>,<y1>:<x2>,<y2> <x>,<y>:<w>x<h> Also: the old geometry behavior (block granularity and map shrinking) is now enabled only if the old geometry format is used (for compatibility)
This commit is contained in:
parent
7bdea466b3
commit
c00cc13e6a
55
README.rst
55
README.rst
@ -127,27 +127,38 @@ backend:
|
|||||||
By default, the backend is 'auto', i.e. it is determined from the backend
|
By default, the backend is 'auto', i.e. it is determined from the backend
|
||||||
setting in the world's world.mt file (if found).
|
setting in the world's world.mt file (if found).
|
||||||
|
|
||||||
geometry <geometry>:
|
centergeometry <geometry>:
|
||||||
(see below, under 'centergeometry')
|
(see below, under 'geometry')
|
||||||
|
|
||||||
cornergeometry <geometry>:
|
cornergeometry <geometry>:
|
||||||
(see below, under 'centergeometry')
|
(see below, under 'geometry')
|
||||||
|
|
||||||
centergeometry <geometry>:
|
geometry <geometry>:
|
||||||
Limit the part of the world that is included in the map.
|
Limit the part of the world that is included in the map.
|
||||||
|
|
||||||
<geometry> has one of the formats:
|
<geometry> has one of the formats:
|
||||||
|
|
||||||
<width>x<height>[<+|-xoffset><+|-yoffset>]
|
<width>x<height>[<+|-xoffset><+|-yoffset>] (dimensions & corner)
|
||||||
|
|
||||||
<xoffset>:<yoffset>+width+height
|
<xoffset>,<yoffset>+width+height (corner & dimensions)
|
||||||
|
|
||||||
For cornergeometry, the offsets will be at the lower-left
|
<xcenter>,<ycenter>:widthxheight (center & dimensions)
|
||||||
corner of the image (offsets increase from left to right,
|
|
||||||
and from bottom to top).
|
|
||||||
|
|
||||||
For centergeometry, the offsets will be in the center of
|
<xcorner1>,<ycorner1>:<xcorner2>,<ycorner2>
|
||||||
the image.
|
|
||||||
|
The old/original format is also supported:
|
||||||
|
|
||||||
|
<xoffset>:<yoffset>+width+height (corner & dimensions)
|
||||||
|
|
||||||
|
For 'cornergeometry', the offsets ([xy]offset or [xy]center) will
|
||||||
|
be at the lower-left corner of the image (offsets increase from left
|
||||||
|
to right, and from bottom to top).
|
||||||
|
|
||||||
|
For 'centergeometry', the offsets ([xy]offset or [xy]center) will be
|
||||||
|
in the center of the image.
|
||||||
|
|
||||||
|
For plain 'geometry', the offsets will be at the corner, or in
|
||||||
|
the center, depending on the geometry format.
|
||||||
|
|
||||||
If the offsets are not specified (with the first format),
|
If the offsets are not specified (with the first format),
|
||||||
the map is centered on the center of the world.
|
the map is centered on the center of the world.
|
||||||
@ -155,21 +166,26 @@ centergeometry <geometry>:
|
|||||||
By default, the geometry has pixel granularity, and a map of
|
By default, the geometry has pixel granularity, and a map of
|
||||||
exactly the requested size is generated.
|
exactly the requested size is generated.
|
||||||
|
|
||||||
Only if the *first* geometry option on the command-line is
|
*Compatibility mode*:
|
||||||
`--geometry`, then for compatibility, the old behavior
|
|
||||||
is default instead (i.e. block granularity, and a smaller
|
If the *first* geometry-related option on the command-line
|
||||||
map if possible). Block granularity is also enabled when
|
is `--geometry`, *and* if the old format is used, then for
|
||||||
the obsolete option '--forcegeometry' is found first.
|
compatibility, the old behavior is default instead (i.e.
|
||||||
|
block granularity, and a smaller map if possible). Block
|
||||||
|
granularity is also enabled when the obsolete (and otherwise
|
||||||
|
undocumented) option '--forcegeometry' is found first.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
`--geometry 10x10-5-5`
|
`--geometry 10x10-5-5`
|
||||||
|
|
||||||
|
`--geometry 100,100:500,1000`
|
||||||
|
|
||||||
`--cornergeometry 50x50+100+100`
|
`--cornergeometry 50x50+100+100`
|
||||||
|
|
||||||
`--centergeometry 1100x1300+1000-500`
|
`--centergeometry 1100x1300+1000-500`
|
||||||
|
|
||||||
`--centergeometry 1100x1300`
|
`--geometry 1100x1300`
|
||||||
|
|
||||||
geometrymode pixel,block,fixed,shrink:
|
geometrymode pixel,block,fixed,shrink:
|
||||||
Specify how the geometry should be interpreted. One or
|
Specify how the geometry should be interpreted. One or
|
||||||
@ -199,6 +215,11 @@ geometrymode fixed:
|
|||||||
Generate a map of the requested geometry, even if part
|
Generate a map of the requested geometry, even if part
|
||||||
or all of it would be empty.
|
or all of it would be empty.
|
||||||
|
|
||||||
|
*NOTE*: If this flag is used, and no actual geometry is
|
||||||
|
specified, this would result in a maximum-size map (65536
|
||||||
|
x 65536), which is currently not possible, and will fail,
|
||||||
|
due to a bug in the drawing library.
|
||||||
|
|
||||||
geometrymode shrink:
|
geometrymode shrink:
|
||||||
Generate a map of at most the requested geometry. Shrink
|
Generate a map of at most the requested geometry. Shrink
|
||||||
it to the smallest possible size that still includes the
|
it to the smallest possible size that still includes the
|
||||||
|
@ -259,40 +259,37 @@ void TileGenerator::enableProgressIndicator(void)
|
|||||||
progressIndicator = true;
|
progressIndicator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileGenerator::setGeometry(int x, int y, int w, int h)
|
void TileGenerator::setGeometry(const NodeCoord &corner1, const NodeCoord &corner2)
|
||||||
{
|
{
|
||||||
if (x > 0) {
|
if (corner1.x > 0) {
|
||||||
m_reqXMin = x / 16;
|
m_reqXMin = corner1.x / 16;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_reqXMin = (x - 15) / 16;
|
m_reqXMin = (corner1.x - 15) / 16;
|
||||||
}
|
}
|
||||||
if (y > 0) {
|
if (corner1.y > 0) {
|
||||||
m_reqZMin = y / 16;
|
m_reqZMin = corner1.y / 16;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_reqZMin = (y - 15) / 16;
|
m_reqZMin = (corner1.y - 15) / 16;
|
||||||
}
|
}
|
||||||
m_mapXStartNodeOffset = x - m_reqXMin * 16;
|
m_mapXStartNodeOffset = corner1.x - m_reqXMin * 16;
|
||||||
m_mapYEndNodeOffset = m_reqZMin * 16 - y;
|
m_mapYEndNodeOffset = m_reqZMin * 16 - corner1.y;
|
||||||
|
|
||||||
int x2 = x + w - 1;
|
if (corner2.x > 0) {
|
||||||
int y2 = y + h - 1;
|
m_reqXMax = corner2.x / 16;
|
||||||
|
|
||||||
if (x2 > 0) {
|
|
||||||
m_reqXMax = x2 / 16;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_reqXMax = (x2 - 15) / 16;
|
m_reqXMax = (corner2.x - 15) / 16;
|
||||||
}
|
}
|
||||||
if (y2 > 0) {
|
if (corner2.y > 0) {
|
||||||
m_reqZMax = y2 / 16;
|
m_reqZMax = corner2.y / 16;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_reqZMax = (y2 - 15) / 16;
|
m_reqZMax = (corner2.y - 15) / 16;
|
||||||
}
|
}
|
||||||
m_mapXEndNodeOffset = x2 - (m_reqXMax * 16 + 15);
|
m_mapXEndNodeOffset = corner2.x - (m_reqXMax * 16 + 15);
|
||||||
m_mapYStartNodeOffset = (m_reqZMax * 16 + 15) - y2;
|
m_mapYStartNodeOffset = (m_reqZMax * 16 + 15) - corner2.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileGenerator::setMinY(int y)
|
void TileGenerator::setMinY(int y)
|
||||||
|
@ -66,7 +66,7 @@ public:
|
|||||||
void setDrawScale(bool drawScale);
|
void setDrawScale(bool drawScale);
|
||||||
void setDrawAlpha(bool drawAlpha);
|
void setDrawAlpha(bool drawAlpha);
|
||||||
void setShading(bool shading);
|
void setShading(bool shading);
|
||||||
void setGeometry(int x, int y, int w, int h);
|
void setGeometry(const NodeCoord &corner1, const NodeCoord &corner2);
|
||||||
void setMinY(int y);
|
void setMinY(int y);
|
||||||
void setMaxY(int y);
|
void setMaxY(int y);
|
||||||
void setShrinkGeometry(bool shrink);
|
void setShrinkGeometry(bool shrink);
|
||||||
|
421
mapper.cpp
421
mapper.cpp
@ -24,6 +24,27 @@ using namespace std;
|
|||||||
#define OPT_SQLITE_CACHEWORLDROW 0x81
|
#define OPT_SQLITE_CACHEWORLDROW 0x81
|
||||||
#define OPT_PROGRESS_INDICATOR 0x82
|
#define OPT_PROGRESS_INDICATOR 0x82
|
||||||
|
|
||||||
|
class FuzzyBool {
|
||||||
|
private:
|
||||||
|
int m_value;
|
||||||
|
FuzzyBool(int i) : m_value(i) {}
|
||||||
|
public:
|
||||||
|
FuzzyBool() : m_value(0) {}
|
||||||
|
FuzzyBool(bool b) : m_value(b ? Yes.m_value : No.m_value) {}
|
||||||
|
static const FuzzyBool Yes;
|
||||||
|
static const FuzzyBool Maybe;
|
||||||
|
static const FuzzyBool No;
|
||||||
|
inline friend bool operator==(FuzzyBool f1, FuzzyBool f2) { return f1.m_value == f2.m_value; }
|
||||||
|
inline friend bool operator!=(FuzzyBool f1, FuzzyBool f2) { return f1.m_value != f2.m_value; }
|
||||||
|
inline friend bool operator>=(FuzzyBool f1, FuzzyBool f2) { return f1.m_value >= f2.m_value; }
|
||||||
|
inline friend bool operator<=(FuzzyBool f1, FuzzyBool f2) { return f1.m_value <= f2.m_value; }
|
||||||
|
inline friend bool operator<(FuzzyBool f1, FuzzyBool f2) { return f1.m_value < f2.m_value; }
|
||||||
|
inline friend bool operator>(FuzzyBool f1, FuzzyBool f2) { return f1.m_value < f2.m_value; }
|
||||||
|
};
|
||||||
|
const FuzzyBool FuzzyBool::Yes = 1;
|
||||||
|
const FuzzyBool FuzzyBool::Maybe = 0;
|
||||||
|
const FuzzyBool FuzzyBool::No = -1;
|
||||||
|
|
||||||
void usage()
|
void usage()
|
||||||
{
|
{
|
||||||
const char *usage_text = "minetestmapper [options]\n"
|
const char *usage_text = "minetestmapper [options]\n"
|
||||||
@ -45,9 +66,14 @@ void usage()
|
|||||||
" --max-y <y>\n"
|
" --max-y <y>\n"
|
||||||
" --backend <" USAGE_DATABASES ">\n"
|
" --backend <" USAGE_DATABASES ">\n"
|
||||||
" --geometry <geometry>\n"
|
" --geometry <geometry>\n"
|
||||||
|
"\t(Warning: has a compatibility mode - see README.rst)\n"
|
||||||
" --cornergeometry <geometry>\n"
|
" --cornergeometry <geometry>\n"
|
||||||
" --centergeometry <geometry>\n"
|
" --centergeometry <geometry>\n"
|
||||||
" --geometrymode pixel,block,fixed,shrink\n"
|
" --geometrymode pixel,block,fixed,shrink\n"
|
||||||
|
"\tpixel: interpret geometry as pixel-accurate\n"
|
||||||
|
"\tblock: round geometry away from zero, to entire map blocks (16 nodes)\n"
|
||||||
|
"\tfixed: generate a map of exactly the requested geometry\n"
|
||||||
|
"\tshrink: generate a smaller map if possible\n"
|
||||||
#if USE_SQLITE3
|
#if USE_SQLITE3
|
||||||
" --sqlite-cacheworldrow\n"
|
" --sqlite-cacheworldrow\n"
|
||||||
#endif
|
#endif
|
||||||
@ -59,8 +85,17 @@ void usage()
|
|||||||
"\t'#000' or '#000000' (RGB)\n"
|
"\t'#000' or '#000000' (RGB)\n"
|
||||||
"\t'#0000' or '#0000000' (ARGB - usable if an alpha value is allowed)\n"
|
"\t'#0000' or '#0000000' (ARGB - usable if an alpha value is allowed)\n"
|
||||||
"Geometry formats:\n"
|
"Geometry formats:\n"
|
||||||
"\t<width>x<heigth>[+|-<xoffset>+|-<yoffset>]\n"
|
"\t<width>x<heigth>[+|-<xoffset>+|-<yoffset>] (dimensions and corner)\n"
|
||||||
"\t<xoffset>:<yoffset>+<width>+<height>\n";
|
"\t<xoffset>,<yoffset>+<width>+<height> (corner and dimensions)\n"
|
||||||
|
"\t<xcenter>,<ycenter>:<width>x<height> (center and dimensions)\n"
|
||||||
|
"\t<xcorner1>,<ycorner1>:<xcorner2>,<ycorner2> (corners of area)\n"
|
||||||
|
"\tOriginal/legacy format - see note under '--geometry' option:\n"
|
||||||
|
"\t<xoffset>:<yoffset>+<width>+<height> (corner and dimensions)\n"
|
||||||
|
"X and Y coordinate formats:\n"
|
||||||
|
"\t[+-]<n> (node +/- <n>)\n"
|
||||||
|
"\t[+-]<b>#[<n>] (node <n> in block +/- <b>)\n"
|
||||||
|
"\t[+-]<b>.[<n>] (node +/- (b * 16 + n))\n"
|
||||||
|
;
|
||||||
std::cout << usage_text;
|
std::cout << usage_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +160,313 @@ void parseColorsFile(TileGenerator &generator, const string &input, string color
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is: stream to read from
|
||||||
|
// coord: set to coordinate value that was read
|
||||||
|
// isBlockCoord: set to true if the coordinate read was a block coordinate
|
||||||
|
// wildcard: if non-zero, accept '*' as a coordinate, and return this value instead.
|
||||||
|
// (suggested values for 'wildcard': INT_MIN or INT_MAX)
|
||||||
|
//
|
||||||
|
// Accepted coordinate syntax:
|
||||||
|
// [+-]<n>: node coordinate: node +/- n
|
||||||
|
// [+-]<b>#: block coordinate: block +/- b (isBlockCoord will be set to true)
|
||||||
|
// [+-]<b>#<n>: node coordinate: node <n> in block +/- <b>
|
||||||
|
// [+-]<b>.<n>: node coordinate: node +/- (b * 16 + n)
|
||||||
|
// As a special feature, double signs are also supported. E.g.:
|
||||||
|
// +-3
|
||||||
|
// Which allows shell command-lines like the following
|
||||||
|
// ${width}x${height}+$xoffs+$yoffs
|
||||||
|
// (which otherwise require special measures to cope with xoffs or yoffs being negative...)
|
||||||
|
// Other uses of this feature are left as an excercise to the reader.
|
||||||
|
// Hint: --3.5 is *not* the same as 3.5
|
||||||
|
static bool parseNodeCoordinate(istream &is, int &coord, bool &isBlockCoord, int wildcard)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int i;
|
||||||
|
char s;
|
||||||
|
|
||||||
|
s = c = is.peek();
|
||||||
|
if (c == '*') {
|
||||||
|
if (wildcard) {
|
||||||
|
i = wildcard;
|
||||||
|
is.ignore(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
is >> coord; // Set stream status to failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wildcard = 0; // not processing a wildcard now
|
||||||
|
if (s == '-' || s == '+')
|
||||||
|
is.ignore(1);
|
||||||
|
else
|
||||||
|
s = '+';
|
||||||
|
is >> i;
|
||||||
|
if (s == '-')
|
||||||
|
i = -i;
|
||||||
|
}
|
||||||
|
if (is.fail())
|
||||||
|
return false;
|
||||||
|
coord = i;
|
||||||
|
isBlockCoord = false;
|
||||||
|
if (is.eof())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if this is a block number, and so: if it has a node number.
|
||||||
|
c = is.peek();
|
||||||
|
if (c == '#' || c == '.') {
|
||||||
|
// coordinate read was a block number
|
||||||
|
is.ignore(1);
|
||||||
|
if (wildcard) {
|
||||||
|
return false; // wildcards are generic
|
||||||
|
}
|
||||||
|
else if (isdigit(is.peek())) {
|
||||||
|
// has a node number / offset
|
||||||
|
is >> i;
|
||||||
|
if (!is.fail()) {
|
||||||
|
if (c == '.' && s == '-') {
|
||||||
|
// Using '.', the node number has same sign as block number
|
||||||
|
// Using '#', the node number is always positive
|
||||||
|
// i.e. -1#1 is: node #1 in block -1 (i.e. node -16 + 1 = -15)
|
||||||
|
// i.e. -1.1 is: 1 block and 1 node in negative direction (i.e. node 16 - 1 = -17)
|
||||||
|
i = -i;
|
||||||
|
}
|
||||||
|
coord = coord * 16 + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No node number / offset
|
||||||
|
isBlockCoord = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (!is.fail());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseCoordinates(istream &is, NodeCoord &coord, int n, int wildcard = 0, char separator = ',')
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
result = true;
|
||||||
|
NodeCoord tempCoord;
|
||||||
|
for (int i = 0; result && i < n; i++) {
|
||||||
|
if (i && separator) {
|
||||||
|
char c;
|
||||||
|
is >> c;
|
||||||
|
if (c != separator) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = parseNodeCoordinate(is, tempCoord.dimension[i], tempCoord.isBlock[i], wildcard);
|
||||||
|
}
|
||||||
|
if (result)
|
||||||
|
coord = tempCoord;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convertBlockToNodeCoordinates(NodeCoord &coord, int offset, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (coord.isBlock[i]) {
|
||||||
|
coord.dimension[i] = coord.dimension[i] * 16 + offset;
|
||||||
|
coord.isBlock[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convertBlockToNodeCoordinates(NodeCoord &coord1, NodeCoord &coord2, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int c1 = coord1.isBlock[i] ? coord1.dimension[i] * 16 : coord1.dimension[i];
|
||||||
|
int c2 = coord2.isBlock[i] ? coord2.dimension[i] * 16 + 15 : coord2.dimension[i];
|
||||||
|
if (c1 > c2) {
|
||||||
|
c1 = coord1.isBlock[i] ? coord1.dimension[i] * 16 + 15 : coord1.dimension[i];
|
||||||
|
c2 = coord2.isBlock[i] ? coord2.dimension[i] * 16 : coord2.dimension[i];
|
||||||
|
}
|
||||||
|
coord1.dimension[i] = c1;
|
||||||
|
coord2.dimension[i] = c2;
|
||||||
|
coord1.isBlock[i] = false;
|
||||||
|
coord2.isBlock[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convertCenterToCornerCoordinates(NodeCoord &coord, NodeCoord &dimensions, int n)
|
||||||
|
{
|
||||||
|
// This results in a slight bias to the negative side.
|
||||||
|
// i.e.: 0,0:2x2 will be -1,-1 .. 0,0 and not 0,0 .. 1,1
|
||||||
|
// The advantage is that e.g. 0#,0#:16x16 selects the 16x16 area that is block 0:
|
||||||
|
// 0#,0#:16x16 -> 0,0:15,15
|
||||||
|
// With a bias to the positive side, that would be:
|
||||||
|
// 0#,0#:16x16 -> 1,1:16,16
|
||||||
|
// Which is counter-intuitive by itself (IMHO :-)
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (dimensions.dimension[i] < 0)
|
||||||
|
coord.dimension[i] += -dimensions.dimension[i] / 2;
|
||||||
|
else
|
||||||
|
coord.dimension[i] -= dimensions.dimension[i] / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convertDimensionToCornerCoordinates(NodeCoord &coord1, NodeCoord &coord2, NodeCoord &dimensions, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (dimensions.dimension[i] < 0)
|
||||||
|
coord2.dimension[i] = coord1.dimension[i] + dimensions.dimension[i] + 1;
|
||||||
|
else
|
||||||
|
coord2.dimension[i] = coord1.dimension[i] + dimensions.dimension[i] - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void orderCoordinateDimensions(NodeCoord &coord1, NodeCoord &coord2, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
if (coord1.dimension[i] > coord2.dimension[i]) {
|
||||||
|
int temp = coord1.dimension[i];
|
||||||
|
coord1.dimension[i] = coord2.dimension[i];
|
||||||
|
coord2.dimension[i] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse the following geometry formats:
|
||||||
|
// <w>x<h>[+<x>+<y>]
|
||||||
|
// (dimensions, and position)
|
||||||
|
// (if x and y are omitted, they default to -w/2 and -h/2)
|
||||||
|
// <x1>,<y1>:<x2>,<y2>
|
||||||
|
// (2 corners of the area)
|
||||||
|
// <x>,<y>:<w>x<h>
|
||||||
|
// (center of the area, and dimensions)
|
||||||
|
// <x>[,:]<y>+<w>+<h>
|
||||||
|
// (corner of the area, and dimensions)
|
||||||
|
static bool parseGeometry(istream &is, NodeCoord &coord1, NodeCoord &coord2, NodeCoord &dimensions, bool &legacy, bool ¢ered, int n, FuzzyBool expectDimensions, int wildcard = 0)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
pos = is.tellg();
|
||||||
|
legacy = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
coord1.dimension[i] = NodeCoord::Invalid;
|
||||||
|
coord2.dimension[i] = NodeCoord::Invalid;
|
||||||
|
dimensions.dimension[i] = NodeCoord::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectDimensions >= FuzzyBool::Maybe && parseCoordinates(is, dimensions, n, 0, 'x')) {
|
||||||
|
convertBlockToNodeCoordinates(dimensions, 0, n);
|
||||||
|
// <w>x<h>[+<x>+<y>]
|
||||||
|
if (is.eof()) {
|
||||||
|
centered = true;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
coord1.dimension[i] = 0;
|
||||||
|
coord1.isBlock[i] = false;
|
||||||
|
}
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
centered = false;
|
||||||
|
if (parseCoordinates(is, coord1, n, 0, '\0')) {
|
||||||
|
convertBlockToNodeCoordinates(coord1, 0, n);
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is.clear();
|
||||||
|
is.seekg(pos);
|
||||||
|
if (wildcard) {
|
||||||
|
coord1.x = coord1.y = coord1.z = 0;
|
||||||
|
}
|
||||||
|
if (parseCoordinates(is, coord1, n, wildcard, ',')) {
|
||||||
|
if (expectDimensions == FuzzyBool::No || (expectDimensions == FuzzyBool::Maybe && (is.eof() || is.peek() == ' ' || is.peek() == '\t'))) {
|
||||||
|
// Just coordinates were specified
|
||||||
|
centered = false;
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
else if (wildcard && (coord1.x == wildcard || coord1.y == wildcard || coord1.z == wildcard)) {
|
||||||
|
// wildcards are only allowed for plain coordinates (i.e. no dimensions)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (is.peek() == ':') {
|
||||||
|
is.ignore(1);
|
||||||
|
pos = is.tellg();
|
||||||
|
if (parseCoordinates(is, coord2, n, 0, ',')) {
|
||||||
|
// <x1>,<y1>:<x2>,<y2>
|
||||||
|
centered = false;
|
||||||
|
convertBlockToNodeCoordinates(coord1, coord2, n);
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
is.clear();
|
||||||
|
is.seekg(pos);
|
||||||
|
if (parseCoordinates(is, dimensions, n, 0, 'x')) {
|
||||||
|
// <x>,<y>:<w>x<h>
|
||||||
|
// (x,y is the center of the area by default)
|
||||||
|
centered = true;
|
||||||
|
convertBlockToNodeCoordinates(coord1, 8, n);
|
||||||
|
convertBlockToNodeCoordinates(dimensions, 0, n);
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// <x>,<y>+<w>+<h>
|
||||||
|
centered = false;
|
||||||
|
if (parseCoordinates(is, dimensions, n, 0, '\0')) {
|
||||||
|
convertBlockToNodeCoordinates(coord1, 0, n);
|
||||||
|
convertBlockToNodeCoordinates(dimensions, 0, n);
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is.clear();
|
||||||
|
is.seekg(pos);
|
||||||
|
if (parseCoordinates(is, coord1, n, 0, ':')) {
|
||||||
|
// <x>:<y>+<w>+<h>
|
||||||
|
legacy = true;
|
||||||
|
centered = false;
|
||||||
|
if (parseCoordinates(is, dimensions, n, 0, '\0')) {
|
||||||
|
convertBlockToNodeCoordinates(coord1, 0, n);
|
||||||
|
convertBlockToNodeCoordinates(dimensions, 0, n);
|
||||||
|
return (is.eof() || is.peek() == ' ' || is.peek() == '\t');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseMapGeometry(istream &is, NodeCoord &coord1, NodeCoord &coord2, bool &legacy, FuzzyBool interpretAsCenter)
|
||||||
|
{
|
||||||
|
NodeCoord dimensions;
|
||||||
|
bool centered;
|
||||||
|
|
||||||
|
bool result = parseGeometry(is, coord1, coord2, dimensions, legacy, centered, 2, FuzzyBool::Yes);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
bool haveCoord2 = coord2.dimension[0] != NodeCoord::Invalid
|
||||||
|
&& coord2.dimension[1] != NodeCoord::Invalid;
|
||||||
|
bool haveDimensions = dimensions.dimension[0] != NodeCoord::Invalid
|
||||||
|
&& dimensions.dimension[1] != NodeCoord::Invalid;
|
||||||
|
if (!haveCoord2 && haveDimensions) {
|
||||||
|
// Convert coord1 + dimensions to coord1 + coord2.
|
||||||
|
// First, if coord1 must be interpreted as center of the area, adjust it to be a corner
|
||||||
|
if ((centered && interpretAsCenter == FuzzyBool::Maybe) || interpretAsCenter == FuzzyBool::Yes)
|
||||||
|
convertCenterToCornerCoordinates(coord1, dimensions, 2);
|
||||||
|
convertDimensionToCornerCoordinates(coord1, coord2, dimensions, 2);
|
||||||
|
}
|
||||||
|
else if (!haveCoord2 || haveDimensions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
orderCoordinateDimensions(coord1, coord2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
static struct option long_options[] =
|
static struct option long_options[] =
|
||||||
@ -284,10 +626,16 @@ int main(int argc, char *argv[])
|
|||||||
case 'T': {
|
case 'T': {
|
||||||
istringstream origin;
|
istringstream origin;
|
||||||
origin.str(optarg);
|
origin.str(optarg);
|
||||||
int x, y;
|
NodeCoord coord;
|
||||||
char c;
|
if (parseCoordinates(origin, coord, 2, 0, ',')) {
|
||||||
origin >> x >> c >> y;
|
convertBlockToNodeCoordinates(coord, 8, 2);
|
||||||
if (origin.fail() || (c != ':' && c != ',')) {
|
generator.setTileOrigin(coord.x, coord.y);
|
||||||
|
}
|
||||||
|
else if (origin.str(optarg), parseCoordinates(origin, coord, 2, 0, ':')) {
|
||||||
|
convertBlockToNodeCoordinates(coord, 8, 2);
|
||||||
|
generator.setTileOrigin(coord.x, coord.y);
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (string("center-world") == optarg)
|
if (string("center-world") == optarg)
|
||||||
generator.setTileOrigin(TILECENTER_IS_WORLDCENTER, TILECENTER_IS_WORLDCENTER);
|
generator.setTileOrigin(TILECENTER_IS_WORLDCENTER, TILECENTER_IS_WORLDCENTER);
|
||||||
else if (string("center-map") == optarg)
|
else if (string("center-map") == optarg)
|
||||||
@ -298,9 +646,6 @@ int main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
generator.setTileOrigin(x, y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
@ -349,9 +694,24 @@ int main(int argc, char *argv[])
|
|||||||
foundGeometrySpec = true;
|
foundGeometrySpec = true;
|
||||||
break;
|
break;
|
||||||
case 'g': {
|
case 'g': {
|
||||||
|
istringstream iss;
|
||||||
|
iss.str(optarg);
|
||||||
|
NodeCoord coord1;
|
||||||
|
NodeCoord coord2;
|
||||||
|
bool legacy;
|
||||||
|
FuzzyBool center = FuzzyBool::Maybe;
|
||||||
|
if (long_options[option_index].name[0] == 'c' && long_options[option_index].name[1] == 'e')
|
||||||
|
center = FuzzyBool::Yes;
|
||||||
|
if (long_options[option_index].name[0] == 'c' && long_options[option_index].name[1] == 'o')
|
||||||
|
center = FuzzyBool::No;
|
||||||
|
if (!parseMapGeometry(iss, coord1, coord2, legacy, center)) {
|
||||||
|
std::cerr << "Invalid geometry specification '" << optarg << "'" << std::endl;
|
||||||
|
usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
// Set defaults
|
// Set defaults
|
||||||
if (!foundGeometrySpec) {
|
if (!foundGeometrySpec) {
|
||||||
if (long_options[option_index].name[0] == 'g') {
|
if (long_options[option_index].name[0] == 'g' && legacy) {
|
||||||
// Compatibility when using the option 'geometry'
|
// Compatibility when using the option 'geometry'
|
||||||
generator.setBlockGeometry(true);
|
generator.setBlockGeometry(true);
|
||||||
generator.setShrinkGeometry(true);
|
generator.setShrinkGeometry(true);
|
||||||
@ -369,46 +729,7 @@ int main(int argc, char *argv[])
|
|||||||
generator.setShrinkGeometry(false);
|
generator.setShrinkGeometry(false);
|
||||||
setFixedOrShrinkGeometry = true;
|
setFixedOrShrinkGeometry = true;
|
||||||
}
|
}
|
||||||
|
generator.setGeometry(coord1, coord2);
|
||||||
istringstream iss;
|
|
||||||
iss.str(optarg);
|
|
||||||
int p1, p2, p3, p4;
|
|
||||||
char c;
|
|
||||||
iss >> p1 >> c >> p2;
|
|
||||||
if (!iss.fail() && c == 'x' && iss.eof()) {
|
|
||||||
p3 = -(p1 / 2);
|
|
||||||
p4 = -(p2 / 2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
char s3, s4;
|
|
||||||
iss >> s3 >> p3 >> s4 >> p4;
|
|
||||||
// accept +-23 as well (for ease of use)
|
|
||||||
if ((s3 != '+' && s3 != '-') || (s4 != '+' && s4 != '-'))
|
|
||||||
c = 0; // Causes an 'invalid geometry' message
|
|
||||||
if (s3 == '-') p3 = -p3;
|
|
||||||
if (s4 == '-') p4 = -p4;
|
|
||||||
if (long_options[option_index].name[0] == 'c'
|
|
||||||
&& long_options[option_index].name[1] == 'e') {
|
|
||||||
// option 'centergeometry'
|
|
||||||
p3 -= p1 / 2;
|
|
||||||
p4 -= p2 / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (iss.fail() || (c != ':' && c != 'x')) {
|
|
||||||
std::cerr << "Invalid geometry specification '" << optarg << "'" << std::endl;
|
|
||||||
usage();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if ((c == ':' && (p3 < 1 || p4 < 1))
|
|
||||||
|| (c == 'x' && (p1 < 1 || p2 < 1))) {
|
|
||||||
std::cerr << "Invalid geometry (width and/or heigth is zero or negative)" << std::endl;
|
|
||||||
usage();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (c == ':')
|
|
||||||
generator.setGeometry(p1, p2, p3, p4);
|
|
||||||
if (c == 'x')
|
|
||||||
generator.setGeometry(p3, p4, p1, p2);
|
|
||||||
foundGeometrySpec = true;
|
foundGeometrySpec = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user