2nd part of last commit's work: Implement a simple math solving

parser that allows simple expressions to be entered for dimensions,
such as "2um + 2um" or even mixtures of units like "3um + 200i".
This feature is currently experimental.
This commit is contained in:
R. Timothy Edwards 2026-01-24 21:02:00 -05:00
parent 9760ef6d1d
commit 0cbed6078a
1 changed files with 177 additions and 94 deletions

View File

@ -73,9 +73,22 @@ TileTypeBitMask CmdYMAllButSpace;
* lambda, a suffix of "g" indicates the user grid, and a suffix in metric * lambda, a suffix of "g" indicates the user grid, and a suffix in metric
* notation ("nm", "um", "mm", "cm") indicates natural units. Other valid * notation ("nm", "um", "mm", "cm") indicates natural units. Other valid
* units are "cu" or "centimicrons" for centimicrons, or "microns" for um. * units are "cu" or "centimicrons" for centimicrons, or "microns" for um.
* Units without any suffix are assumed to be in lambda if "snap" * Traditional (backwards-compatible) behavior: Units without any suffix
* (DBWSnapToGrid) is set to lambda, grid units if "snap" is set to the * are assumed to be in lambda if "snap" (DBWSnapToGrid) is set to lambda,
* user grid, and internal units otherwise. * grid units if "snap" is set to the user grid, and internal units otherwise.
* Current behavior: Use of the "units" command to set the units to
* any value other than "default" causes cmdScaleCoord() to parse any
* units provided without an identifying suffix as the units indicted by
* the "units" command. Once the "units" command has been issued, the
* values are dependent on DBWUnits and not on DBWSnapToGrid.
*
* Additional behavior from magic version 8.3.596: A single command
* option can use simple expressions using '+', '-', '*', and '/'. These
* can be passed as a single token, without spaces, or within a string
* token deliniated by quotes or braces, per usual Tcl syntax. Unlike
* the Tcl "expr" command, this can solve arithmetic expressions of
* suffixed values, evaluated independently such that different suffixes
* may be used (e.g., "1g + 3um" meaning 1 grid pitch plus 3 microns).
* *
* MagWindow argument w is used only with grid-based snapping, to find * MagWindow argument w is used only with grid-based snapping, to find
* the value of the grid for the given window. In this case, because the * the value of the grid for the given window. In this case, because the
@ -99,6 +112,13 @@ TileTypeBitMask CmdYMAllButSpace;
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
*/ */
#define PARSEOP_NONE 0
#define PARSEOP_ADD 1
#define PARSEOP_SUB 2
#define PARSEOP_MUL 3
#define PARSEOP_DIV 4
#define PARSEOP_END 5
int int
cmdScaleCoord( cmdScaleCoord(
MagWindow *w, MagWindow *w,
@ -110,19 +130,26 @@ cmdScaleCoord(
char *endptr; char *endptr;
double dval = 0; double dval = 0;
int mscale = 1, curunits; int mscale = 1, curunits;
int retval, curval, parseop;
DBWclientRec *crec; DBWclientRec *crec;
if (*arg == '{') arg++; if (*arg == '{' || *arg == '"') arg++;
while (isspace(*arg)) arg++; while (isspace(*arg) && (*arg != '\0')) arg++;
parseop = PARSEOP_NONE;
retval = 0;
while (*arg != '\0')
{
dval = strtod(arg, &endptr); dval = strtod(arg, &endptr);
dval *= (double)scale; dval *= (double)scale;
mscale = -1;
if (endptr == arg) if (endptr == arg)
{ {
/* strtod() error condition */ /* strtod() error condition */
TxError("Coordinate value cannot be parsed: assuming 0\n"); TxError("Coordinate value cannot be parsed: assuming 0\n");
return 0; curval = 0;
break;
} }
/* Original behavior was to accept un-suffixed values according to the /* Original behavior was to accept un-suffixed values according to the
@ -141,13 +168,11 @@ cmdScaleCoord(
/* lambda or default units */ /* lambda or default units */
dval *= (double)DBLambda[1]; dval *= (double)DBLambda[1];
dval /= (double)DBLambda[0]; dval /= (double)DBLambda[0];
return round(dval);
} }
else if ((*endptr == 'i') else if ((*endptr == 'i')
|| ((*endptr == '\0') && (curunits == DBW_UNITS_INTERNAL))) || ((*endptr == '\0') && (curunits == DBW_UNITS_INTERNAL)))
{ {
/* internal units */ /* internal units */
return round(dval);
} }
else if ((*endptr == 'g') else if ((*endptr == 'g')
|| ((*endptr == '\0') && (curunits == DBW_UNITS_USER))) || ((*endptr == '\0') && (curunits == DBW_UNITS_USER)))
@ -157,7 +182,10 @@ cmdScaleCoord(
{ {
windCheckOnlyWindow(&w, DBWclientID); windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *)NULL) if (w == (MagWindow *)NULL)
return round(dval); /* Default, if window is unknown */ {
curval = round(dval); /* Default, if window is unknown */
break;
}
} }
crec = (DBWclientRec *) w->w_clientData; crec = (DBWclientRec *) w->w_clientData;
if (is_x) if (is_x)
@ -174,7 +202,6 @@ cmdScaleCoord(
if (!is_relative) if (!is_relative)
dval += (double)crec->dbw_gridRect.r_ybot; dval += (double)crec->dbw_gridRect.r_ybot;
} }
return round(dval);
} }
else if (*endptr == '\0' && (curunits == DBW_UNITS_MICRONS)) else if (*endptr == '\0' && (curunits == DBW_UNITS_MICRONS))
{ {
@ -200,12 +227,12 @@ cmdScaleCoord(
mscale = 10000000; mscale = 10000000;
break; break;
default: default:
TxError("Unknown metric prefix \"%cm\"; assuming internal units\n", TxError("Unknown metric prefix \"%cm\"; assuming "
*endptr); "internal units\n", *endptr);
return round(dval); mscale = -1;
} }
} }
else if (!strcmp(endptr, "u")) else if ((*endptr == 'u') && !isalnum(*(endptr + 1)))
/* Maybe "u" is too ambiguous but it is very commonly used as /* Maybe "u" is too ambiguous but it is very commonly used as
* an abbreviation for "micron". * an abbreviation for "micron".
*/ */
@ -214,16 +241,72 @@ cmdScaleCoord(
mscale = 1000; mscale = 1000;
else if (!strncmp(endptr, "centimicron", 11) || !strcmp(endptr, "cu")) else if (!strncmp(endptr, "centimicron", 11) || !strcmp(endptr, "cu"))
mscale = 10; mscale = 10;
else if (!isspace(*endptr)) else if (!isspace(*endptr) && (*endptr != '+') && (*endptr != '-') &&
(*endptr != '*') && (*endptr != '/'))
{ {
TxError("Unknown coordinate type \"%s\"; assuming internal units\n", TxError("Unknown coordinate type at \"%s\"; assuming internal units\n",
endptr); endptr);
return round(dval); mscale = -1;
} }
} }
if (!isspace(*endptr)) if ((mscale != -1) && !isspace(*endptr))
dval /= CIFGetOutputScale(mscale); dval /= CIFGetOutputScale(mscale);
return round(dval); curval = round(dval);
switch (parseop)
{
case PARSEOP_NONE:
retval = curval;
break;
case PARSEOP_ADD:
retval += curval;
break;
case PARSEOP_SUB:
retval -= curval;
break;
case PARSEOP_MUL:
retval *= curval;
break;
case PARSEOP_DIV:
retval /= curval;
break;
}
parseop = PARSEOP_NONE;
while (*endptr != '\0')
{
switch (*endptr)
{
case '}':
case '"':
parseop = PARSEOP_END;
break;
case '+':
parseop = PARSEOP_ADD;
endptr++;
break;
case '-':
parseop = PARSEOP_SUB;
endptr++;
break;
case '*':
parseop = PARSEOP_MUL;
endptr++;
break;
case '/':
parseop = PARSEOP_DIV;
endptr++;
break;
default:
endptr++;
break;
}
if (parseop != PARSEOP_NONE) break;
}
arg = endptr;
while (isspace(*arg) && (*arg != '\0')) arg++;
}
return retval;
} }
/* /*