about summary refs log tree commit diff
path: root/generator/pamtris/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'generator/pamtris/input.c')
-rw-r--r--generator/pamtris/input.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/generator/pamtris/input.c b/generator/pamtris/input.c
new file mode 100644
index 00000000..ffb2a859
--- /dev/null
+++ b/generator/pamtris/input.c
@@ -0,0 +1,695 @@
+/*=============================================================================
+                              input.c
+===============================================================================
+   Input handling functions
+=============================================================================*/
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm.h"
+#include "netpbm/nstring.h"
+
+#include "limits_pamtris.h"
+#include "framebuffer.h"
+#include "triangle.h"
+
+#include "input.h"
+
+#define DRAW_MODE_TRIANGLES 1
+#define DRAW_MODE_STRIP     2
+#define DRAW_MODE_FAN       3
+
+#define CMD_SET_MODE        "mode"
+#define CMD_SET_ATTRIBS     "attribs"
+#define CMD_VERTEX          "vertex"
+#define CMD_PRINT           "print"
+#define CMD_CLEAR           "clear"
+#define CMD_RESET           "reset"
+#define CMD_QUIT            "quit"
+
+#define ARG_TRIANGLES       "triangles"
+#define ARG_STRIP           "strip"
+#define ARG_FAN             "fan"
+#define ARG_IMAGE           "image"
+#define ARG_DEPTH           "depth"
+
+
+typedef struct {
+    Xy v_xy;
+        /* X- and Y-coordinates of the vertices for the current triangle.
+        */
+    Attribs v_attribs;
+        /* Vertex attributes for the current triangle. Includes the
+           Z-coordinates.
+        */
+    int32_t curr_attribs[MAX_NUM_ATTRIBS];
+        /* Attributes that will be assigned to the next vertex. Does not
+           include the Z-coordinate.
+        */
+    uint8_t next;
+        /* Index of the next vertex to be read. */
+    bool draw;
+        /* If true, draws a new triangle upon reading a new vertex. */
+
+    uint8_t mode;
+        /* Drawing mode. */
+
+    bool initialized;
+} state_info;
+
+
+
+static void
+clearAttribs(state_info * const si,
+             int32_t      const maxval,
+             int16_t      const num_attribs) {
+
+    unsigned int i;
+
+    for (i = 0; i < num_attribs; ++i) {
+        si->curr_attribs[i] = maxval;
+    }
+}
+
+
+
+void
+input_init(Input * const inputP) {
+
+    inputP->buffer = NULL;
+    inputP->length = 0;
+    inputP->number = 1;
+}
+
+
+
+void
+input_term(Input * const inputP) {
+
+    if (inputP->buffer)
+        free(inputP->buffer);
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  Indicates a whitespace-delimited input symbol. "begin" points to its first
+  character, and "end" points to one position past its last character.
+-----------------------------------------------------------------------------*/
+    char * begin;
+    char * end;
+} Token;
+
+
+
+static Token
+nextToken(char * const startPos) {
+
+    Token retval;
+    char * p;
+
+    for (p = startPos; *p && isspace(*p); ++p);
+
+    retval.begin = p;
+
+    for (; *p && !isspace(*p); ++p);
+
+    retval.end = p;
+
+    return retval;
+}
+
+
+
+static bool
+stringIsValid(const char * const target,
+              const char * const srcBegin,
+              const char * const srcEnd) {
+
+    unsigned int charsMatched;
+    const char * p;
+
+    for (p = srcBegin, charsMatched = 0;
+         p != srcEnd && target[charsMatched] != '\0'; ++p) {
+
+        if (*p == target[charsMatched])
+            ++charsMatched;
+        else
+            break;
+    }
+
+    return (*p == '\0' || isspace(*p));
+}
+
+
+
+static void
+initState(state_info * const siP) {
+
+    siP->next = 0;
+    siP->draw = false;
+    siP->mode = DRAW_MODE_TRIANGLES;
+}
+
+
+
+static void
+makeLowercase(Token const t) {
+
+    char * p;
+
+    for (p = t.begin; p != t.end; ++p)
+        *p = tolower(*p);
+}
+
+
+
+static void
+removeComments(char * const str) {
+
+    char * p;
+
+    for (p = &str[0]; *p; ++p) {
+        if (*p == '#') {
+            *p = '\0';
+
+            break;
+        }
+    }
+}
+
+
+
+static void
+processM(Token *       const ntP,
+         state_info *  const stateP,
+         bool *        const unrecognizedCmdP,
+         const char ** const errorP) {
+
+    if (!stringIsValid(CMD_SET_MODE, ntP->begin, ntP->end)) {
+        *unrecognizedCmdP = true;
+    } else {
+        *ntP = nextToken(ntP->end);
+
+        *unrecognizedCmdP = false;
+
+        if (*ntP->begin == '\0')
+            pm_asprintf(errorP, "syntax error");
+        else {
+            makeLowercase(*ntP);
+
+            switch (*ntP->begin) {
+            case 't':
+                if (!stringIsValid(ARG_TRIANGLES, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_TRIANGLES;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            case 's':
+                if (!stringIsValid(ARG_STRIP, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_STRIP;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            case 'f':
+                if (!stringIsValid(ARG_FAN, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_FAN;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            default:
+                pm_asprintf(errorP, "unrecognized drawing mode");
+            }
+        }
+    }
+}
+
+
+
+static void
+processA(Token *            const ntP,
+         state_info *       const stateP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         long int *         const iArgs,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_SET_ATTRIBS, ntP->begin, ntP->end)) {
+        *unrecognizedCmdP = true;
+    } else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < fbiP->num_attribs && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0' || strtolEnd != ntP->end)
+                pm_asprintf(errorP, "syntax error");
+            else {
+                if (iArgs[i] < 0 || iArgs[i] > fbiP->maxval)
+                    pm_asprintf(errorP, "argument(s) out of bounds");
+            }
+        }
+
+        if (!*errorP) {
+            unsigned int i;
+
+            for (i = 0; i < fbiP->num_attribs; ++i)
+                stateP->curr_attribs[i] = iArgs[i];
+        }
+    }
+}
+
+
+
+static void
+processV(Token *                const ntP,
+         state_info *           const stateP,
+         struct boundary_info * const biP,
+         framebuffer_info *     const fbiP,
+         bool *                 const unrecognizedCmdP,
+         long int *             const iArgs,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_VERTEX, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < 4 && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0') {
+                if (i != 3)
+                    pm_asprintf(errorP, "syntax error");
+                else
+                    iArgs[i] = 1;
+            } else {
+                if (strtolEnd != ntP->end)
+                    pm_asprintf(errorP, "syntax error");
+            }
+
+            if (!*errorP) {
+                if (i < 3) {
+                    if (iArgs[i] < MIN_COORD || iArgs[i] > MAX_COORD)
+                        pm_asprintf(errorP, "coordinates out of bounds");
+                } else {
+                    if (iArgs[i] < MIN_INPUT_W || iArgs[i] > MAX_INPUT_W)
+                        pm_asprintf(errorP,
+                                    "perspective correction factor (w) "
+                                    "out of bounds");
+                }
+            }
+        }
+
+        if (!*errorP) {
+            unsigned int i;
+
+            for (i = 0; i < fbiP->num_attribs; ++i) {
+                stateP->v_attribs._[stateP->next][i] = stateP->curr_attribs[i];
+            }
+
+            stateP->v_attribs._[stateP->next][fbiP->num_attribs + 0] =
+                iArgs[2];
+            stateP->v_attribs._[stateP->next][fbiP->num_attribs + 1] =
+                iArgs[3];
+
+            stateP->v_xy._[stateP->next][0] = iArgs[0];
+            stateP->v_xy._[stateP->next][1] = iArgs[1];
+
+            ++stateP->next;
+
+            if (!stateP->draw) {
+                if (stateP->next == 3)
+                    stateP->draw = true;
+            }
+
+            if (stateP->draw)
+                draw_triangle(stateP->v_xy, stateP->v_attribs, biP, fbiP);
+
+            if (stateP->next == 3) {
+                switch(stateP->mode) {
+                case DRAW_MODE_FAN:
+                    stateP->next = 1;
+                    break;
+                case DRAW_MODE_TRIANGLES:
+                    stateP->draw = false;
+                    stateP->next = 0;
+                    break;
+                case DRAW_MODE_STRIP:
+                    stateP->next = 0;
+                    break;
+                default:
+                    stateP->next = 0;
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+processP(Token *            const ntP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_PRINT, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        print_framebuffer(fbiP);
+
+        *errorP = NULL;
+    }
+}
+
+
+
+
+static void
+processExcl(Token *            const ntP,
+            framebuffer_info * const fbiP,
+            bool *             const unrecognizedCmdP,
+            const char **      const errorP) {
+
+    if (ntP->end - ntP->begin > 1)
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        print_framebuffer(fbiP);
+
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
+clear(Token *            const ntP,
+      framebuffer_info * const fbiP,
+      const char **      const errorP) {
+
+    *ntP = nextToken(ntP->end);
+
+    if (*ntP->begin != '\0') {
+        makeLowercase(*ntP);
+
+        switch(*ntP->begin) {
+        case 'i':
+            if (!stringIsValid("image", ntP->begin, ntP->end))
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(true, false, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        case 'd':
+            if (!stringIsValid("depth", ntP->begin, ntP->end))
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(false, true, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        case 'z':
+            if (ntP->end - ntP->begin > 1)
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(false, true, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        default:
+            pm_asprintf(errorP, "unrecognized argument");
+        }
+    } else {
+        clear_framebuffer(true, true, fbiP);
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
+processC(Token *            const ntP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_CLEAR, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        clear(ntP, fbiP, errorP);
+    }
+}
+
+
+
+static void
+processAsterisk(Token *            const ntP,
+                framebuffer_info * const fbiP,
+                bool *             const unrecognizedCmdP,
+                const char **      const errorP) {
+
+    if (ntP->end - ntP->begin > 1)
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        clear(ntP, fbiP, errorP);
+    }
+}
+
+
+
+static void
+processR(Token *                const ntP,
+         state_info *           const stateP,
+         framebuffer_info *     const fbiP,
+         bool *                 const unrecognizedCmdP,
+         long int *             const iArgs,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_RESET, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < 2 && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0' || ntP->end != strtolEnd)
+                pm_asprintf(errorP, "syntax error");
+        }
+
+        if (!*errorP) {
+            if (iArgs[0] < 1 || iArgs[0] > PAM_OVERALL_MAXVAL)
+                pm_asprintf(errorP, "invalid new maxval");
+            else {
+                if (iArgs[1] < 1 || iArgs[1] > MAX_NUM_ATTRIBS)
+                    pm_asprintf(errorP, "invalid new number of generic vertex "
+                                "attributes");
+                else {
+                    *ntP = nextToken(ntP->end);
+
+                    if (*ntP->begin != '\0') {
+                        if (!set_tupletype(ntP->begin,
+                                           fbiP->outpam.tuple_type)) {
+                            pm_message(
+                                "warning: could not set new tuple type; "
+                                "using a null string");
+                            set_tupletype(NULL, fbiP->outpam.tuple_type);
+                        }
+                    } else
+                        set_tupletype(NULL, fbiP->outpam.tuple_type);
+
+                    if (!realloc_image_buffer(iArgs[0], iArgs[1], fbiP)) {
+                        pm_error("Unable to allocate memory for "
+                                 "image buffer");
+                    }
+
+                    stateP->next = 0;
+                    stateP->draw = false;
+
+                    clearAttribs(stateP, fbiP->maxval, fbiP->num_attribs);
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+processQ(Token *                const ntP,
+         bool *                 const unrecognizedCmdP,
+         bool *                 const noMoreCommandsP,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_QUIT, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        *noMoreCommandsP = true;
+
+        *errorP = NULL;
+    }
+}
+
+
+
+void
+input_process_next_command(Input *                const inputP,
+                           struct boundary_info * const biP,
+                           framebuffer_info *     const fbiP,
+                           bool *                 const noMoreCommandsP) {
+/*----------------------------------------------------------------------------
+  Doesn't necessarily process a command, just the next line of input, which
+  may be empty.
+
+  Return *noMoreCommandsP true iff the next command is a quit command of
+  there is no next command.
+-----------------------------------------------------------------------------*/
+    static state_info state;
+
+    Token nt;
+
+    long int iArgs[MAX_NUM_ATTRIBS];
+        /* For storing potential integer arguments. */
+    bool unrecognizedCmd;
+        /* Unrecognized command detected */
+    bool noMoreCommands;
+    const char * error;
+        /* Description of problem with the command; NULL if no problem.
+           Meaningful only when 'unrecognizedCmd' is false.
+        */
+
+    if (!state.initialized) {
+        initState(&state);
+        clearAttribs(&state, fbiP->maxval, fbiP->num_attribs);
+
+        state.initialized = true;
+    }
+
+    {
+        int eof;
+        size_t lineLen;
+
+        pm_getline(stdin, &inputP->buffer, &inputP->length, &eof, &lineLen);
+
+        if (eof) {
+            *noMoreCommandsP = true;
+            return;
+        }
+    }
+
+    removeComments(inputP->buffer);
+
+    nt = nextToken(inputP->buffer);
+
+    makeLowercase(nt);
+
+    noMoreCommands = false;  /* initial assumption */
+
+    switch (nt.begin[0]) {
+    case 'm':
+        processM(&nt, &state, &unrecognizedCmd, &error);
+        break;
+    case 'a':
+        processA(&nt, &state, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'v':
+        processV(&nt, &state, biP, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'p':
+        processP(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case '!':
+        processExcl(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case 'c':
+        processC(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case '*':
+        processAsterisk(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case 'r':
+        processR(&nt, &state, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'q':
+        processQ(&nt, &unrecognizedCmd, &noMoreCommands, &error);
+        break;
+    case '\0':
+        break;
+    default:
+        unrecognizedCmd = true;
+    }
+
+    if (!noMoreCommands) {
+        char const next = *nextToken(nt.end).begin;
+
+        if (unrecognizedCmd) {
+            pm_errormsg("error: unrecognized command: line %u.",
+                        (unsigned)inputP->number);
+        } else {
+            if (error) {
+                pm_errormsg("Error in line %u: %s",
+                            (unsigned)inputP->number, error);
+                pm_strfree(error);
+            } else {
+                if (next != '\0')
+                    pm_message("warning: ignoring excess arguments: line %u",
+                               (unsigned)inputP->number);
+            }
+        }
+    }
+    ++inputP->number;
+
+    *noMoreCommandsP = noMoreCommands;
+}
+
+