nuttx-apps/graphics/traveler/tools/tcledit/tcl_edit.c

735 lines
20 KiB
C

/****************************************************************************
* apps/graphics/traveler/tools/tcledit/tcl_edit.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <tk.h>
#include "trv_types.h"
#include "debug.h"
#include "astplane.h"
#include "astutils.h"
#include "tcl_x11graphics.h"
#include "tcl_colors.h"
/****************************************************************************
* Private Variables
***************************************************************************/
static Tcl_Interp *astInterp;
static const char *astInFileName;
static const char *astOutFileName;
static const char astDefaultFileName[] = "planes.pll";
/****************************************************************************
* Public Variables
***************************************************************************/
enum editModeEnum editMode = EDITMODE_NONE;
enum editPlaneEnum editPlane = EDITPLANE_X;
int viewSize = WORLD_INFINITY;
int gridStep = WORLD_SIZE / 16;
int coordOffset[NUM_PLANES];
int planePosition[NUM_PLANES];
astWindowType windows[NUM_PLANES] =
{
{
.title = "X-Plane",
.plane = EDITPLANE_X,
.width = WINDOW_SIZE,
.height = WINDOW_SIZE,
.palette = {
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
LightGray, DarkGray, DarkGray, DarkGray,
LightSteelBlue, SlateGray, SlateGray, SteelBlue,
},
},
{
.title = "Y-Plane",
.plane = EDITPLANE_Y,
.width = WINDOW_SIZE,
.height = WINDOW_SIZE,
.palette = {
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
LightGray, DarkGray, DarkGray, DarkGray,
LightSteelBlue, SlateGray, SlateGray, SteelBlue,
},
},
{
.title = "Z-Plane",
.plane = EDITPLANE_Z,
.width = WINDOW_SIZE,
.height = WINDOW_SIZE,
.palette = {
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
Lavender, Red, Red, LemonChiffon,
LightCyan, LightSkyBlue, DeepSkyBlue, LightGray,
LightGray, DarkGray, DarkGray, DarkGray,
LightSteelBlue, SlateGray, SlateGray, SteelBlue,
},
},
};
rectDataType editRect;
/****************************************************************************
* Private Functions
***************************************************************************/
/* Called when any change is made to the display while in POS mode */
static void astUpdatePOSModeDisplay(void)
{
int i;
for (i = 0; i < NUM_PLANES; i++)
{
astWindowType *w = &windows[i];
astPaintBackground(w);
astPaintGrid(w);
astPaintRectangles(w);
astPaintPosition(w);
astUpdateScreen(w);
}
}
/* Called when any change is made to the display while in NEW mode */
static void astUpdateNEWModeDisplay(void)
{
int i;
for (i = 0; i < NUM_PLANES; i++)
{
astWindowType *w = &windows[i];
astPaintBackground(w);
astPaintGrid(w);
astPaintRectangles(w);
astUpdateScreen(w);
}
}
/* Called in response to the "ast_seteditmode" Tcl command to set the
* current edit mode
*/
static int astSetEditMode(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
ginfo("Processing command: %s\n", argv[0]);
if (argc != 3)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
else if (strcmp(argv[1], "POS") == 0)
{
ginfo("Entering POS mode\n");
editMode = EDITMODE_POS;
astUpdatePOSModeDisplay();
}
else if (strcmp(argv[1], "NEW") == 0)
{
editMode = EDITMODE_NEW;
memset(&editRect, 0, sizeof(rectDataType));
if (strcmp(argv[2], "x") == 0)
{
ginfo("Entering NEWX mode\n");
editPlane = EDITPLANE_X;
editRect.plane = planePosition[EDITPLANE_X];
editRect.hStart = planePosition[EDITPLANE_Y];
editRect.hEnd = editRect.hStart;
editRect.vStart = planePosition[EDITPLANE_Z];
editRect.vEnd = editRect.vStart;
}
else if (strcmp(argv[2], "y") == 0)
{
ginfo("Entering NEWY mode\n");
editPlane = EDITPLANE_Y;
editRect.plane = planePosition[EDITPLANE_Y];
editRect.hStart = planePosition[EDITPLANE_X];
editRect.hEnd = editRect.hStart;
editRect.vStart = planePosition[EDITPLANE_Z];
editRect.vEnd = editRect.vStart;
}
else if (strcmp(argv[2], "z") == 0)
{
ginfo("Entering NEWZ mode\n");
editPlane = EDITPLANE_Z;
editRect.plane = planePosition[EDITPLANE_Z];
editRect.hStart = planePosition[EDITPLANE_X];
editRect.hEnd = editRect.hStart;
editRect.vStart = planePosition[EDITPLANE_Y];
editRect.vEnd = editRect.vStart;
}
else
{
astFatalError("%s: Unrecognized NEW plane: %s\n",
__FUNCTION__, argv[2]);
}
astUpdateNEWModeDisplay();
}
else
{
astFatalError("%s: Unrecognized mode: %s\n",
__FUNCTION__, argv[1]);
}
return TCL_OK;
}
/* Called in response to the "ast_position" Tcl command */
static int astNewPosition(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
ginfo("Processing command: %s\n", argv[0]);
if (argc != 4)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
planePosition[0] = atoi(argv[1]);
planePosition[1] = atoi(argv[2]);
planePosition[2] = atoi(argv[3]);
ginfo("New plane positions: {%d,%d,%d}\n",
planePosition[0], planePosition[1], planePosition[2]);
astUpdatePOSModeDisplay();
return TCL_OK;
}
/* Called in response to the "ast_zoom" Tcl command */
static int astNewZoom(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
ginfo("Processing command: %s\n", argv[0]);
if (argc != 5)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
/* Get the zoom settings */
viewSize = atoi(argv[1]);
coordOffset[0] = atoi(argv[2]);
coordOffset[1] = atoi(argv[3]);
coordOffset[2] = atoi(argv[4]);
/* The grid size will be determined by the scaling */
if (viewSize <= 256)
{
gridStep = 16; /* 16 lines at 256 */
}
else if (viewSize <= 512)
{
gridStep = 32; /* 16 lines at 512 */
}
else if (viewSize <= 1024)
{
gridStep = 64; /* 16 lines at 1024 */
}
else if (viewSize <= 2048)
{
gridStep = 128; /* 16 lines at 2048 */
}
else if (viewSize <= 4096)
{
gridStep = 256; /* 16 lines at 4096 */
}
else if (viewSize <= 8192)
{
gridStep = 512; /* 16 lines at 8196 */
}
else if (viewSize <= 16384)
{
gridStep = 1024; /* 16 lines at 16384 */
}
else /* if (viewSize <= 32768) */
{
gridStep = 2048; /* 16 lines at 32768 */
}
ginfo("New viewSize, gridStep: %d, %d\n", viewSize, gridStep);
ginfo("New coordinate offsets: {%d,%d,%d}\n",
coordOffset[0], coordOffset[1], coordOffset[2]);
if (editMode == EDITMODE_POS)
{
astUpdatePOSModeDisplay();
}
else
{
astUpdateNEWModeDisplay();
}
return TCL_OK;
}
/* Called in response to the "ast_edit" Tcl command */
static int astNewEdit(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
int start;
int end;
int extent;
ginfo("Processing command: %s\n", argv[0]);
if (argc != 4)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
/* Ignore the command if we are not in NEW mode */
if (editMode == EDITMODE_NEW)
{
/* Get the new position information */
start = atoi(argv[2]);
extent = atoi(argv[3]);
end = start + extent - 1;
/* Which plane are we editting? */
switch (editPlane)
{
case EDITPLANE_X:
if (strcmp(argv[1], "x") == 0)
{
ginfo("New X plane position: %d\n", start);
editRect.plane = start;
}
else if (strcmp(argv[1], "y") == 0)
{
ginfo("New horizontal Y coordinates: {%d,%d}\n", start, end);
editRect.hStart = start;
editRect.hEnd = end;
}
else if (strcmp(argv[1], "z") == 0)
{
ginfo("New vertical Z coordinates: {%d,%d}\n", start, end);
editRect.vStart = start;
editRect.vEnd = end;
}
else
{
astFatalError("%s: Unrecognized EDITX plane: %s\n",
__FUNCTION__, argv[1]);
}
break;
case EDITPLANE_Y:
if (strcmp(argv[1], "x") == 0)
{
ginfo("New horizontal X coordinates: {%d,%d}\n", start, end);
editRect.hStart = start;
editRect.hEnd = end;
}
else if (strcmp(argv[1], "y") == 0)
{
ginfo("New Y plane position: %d\n", start);
editRect.plane = start;
}
else if (strcmp(argv[1], "z") == 0)
{
ginfo("New vertical Z coordinates: {%d,%d}\n", start, end);
editRect.vStart = start;
editRect.vEnd = end;
}
else
{
astFatalError("%s: Unrecognized EDITY plane: %s\n",
__FUNCTION__, argv[1]);
}
break;
case EDITPLANE_Z:
if (strcmp(argv[1], "x") == 0)
{
ginfo("New horizontal X coordinates: {%d,%d}\n", start, end);
editRect.hStart = start;
editRect.hEnd = end;
}
else if (strcmp(argv[1], "y") == 0)
{
ginfo("New vertical Y coordinates: {%d,%d}\n", start, end);
editRect.vStart = start;
editRect.vEnd = end;
}
else if (strcmp(argv[1], "z") == 0)
{
ginfo("New Z plane position: %d\n", start);
editRect.plane = start;
}
else
{
astFatalError("%s: Unrecognized EDITZ plane: %s\n",
__FUNCTION__, argv[1]);
}
break;
default:
break;
}
astUpdateNEWModeDisplay();
}
return TCL_OK;
}
/* Called in response to the "ast_attribute" Tcl command */
static int astNewAttributes(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
const char *attributes;
int tmp;
ginfo("Processing command: %s\n", argv[0]);
if (argc != 4)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
attributes = argv[1];
if (strlen(attributes) != 3)
{
astFatalError("%s: Unexpected attribute string length: %s\n",
__FUNCTION__, argv[1]);
}
/* Ignore the command if we are not in NEW mode */
if (editMode == EDITMODE_NEW)
{
if (attributes[0] == '1')
{
editRect.attribute |= SHADED_PLANE;
}
else
{
editRect.attribute &= ~SHADED_PLANE;
}
if (attributes[1] == '1')
{
editRect.attribute |= TRANSPARENT_PLANE;
}
else
{
editRect.attribute &= ~TRANSPARENT_PLANE;
}
if (attributes[2] == '1')
{
editRect.attribute |= DOOR_PLANE;
}
else
{
editRect.attribute &= ~DOOR_PLANE;
}
ginfo("attributes: %s->%02x\n", attributes, editRect.attribute);
tmp = atoi(argv[2]);
if ((tmp >=0) && (tmp < 256))
{
editRect.texture = tmp;
}
else
{
fprintf(stderr, "Texture index out of range: %d\n", tmp);
}
ginfo("texture: %s->%d\n", argv[2], editRect.texture);
tmp = atoi(argv[3]);
if ((tmp >=0) && (tmp <= MAXX_SCALING))
{
editRect.scale = tmp;
}
else
{
fprintf(stderr, "Scaling not supported: %d\n", tmp);
}
ginfo("scale: %s->%d\n", argv[3], editRect.scale);
}
return TCL_OK;
}
/* Called in response to the "ast_addrectangle" Tcl command */
static int astAddRectangle(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
ginfo("Processing command: %s\n", argv[0]);
if (argc != 1)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
/* Ignore the command if we are not in NEW mode */
if (editMode == EDITMODE_NEW)
{
/* Get a new container for the rectangle information */
rectListType *rect = astNewPlane();
/* Copy the rectangle data into the container */
rect->d = editRect;
/* Save the rectangle in the correct list */
switch (editPlane)
{
case EDITPLANE_X:
astAddPlane(rect, &xPlane);
break;
case EDITPLANE_Y:
astAddPlane(rect, &yPlane);
break;
case EDITPLANE_Z:
astAddPlane(rect, &zPlane);
break;
}
}
return TCL_OK;
}
/* Called in response to the "ast_save" Tcl command */
static int astSaveRectangles(ClientData clientData,
Tcl_Interp *interp,
int argc, const char *argv[])
{
ginfo("Processing command: %s\n", argv[0]);
if (argc != 1)
{
astFatalError("%s: Unexpected number of arguments: %d\n",
__FUNCTION__, argc);
}
astSavePlanes(astOutFileName);
return TCL_OK;
}
static void show_usage(const char *progname)
{
fprintf(stderr, "USAGE:\n\t%s [-o <outfilename>] <infilename>\n",
progname);
exit(1);
}
/*************************************************************************
* Public Functions
************************************************************************/
int main(int argc, char **argv, char **envp)
{
int option;
/* Parse command line options */
astOutFileName = astDefaultFileName;
while ((option = getopt(argc, argv, "o:")) != EOF)
{
switch (option)
{
case 'o' :
astOutFileName = optarg;
break;
default:
fprintf(stderr, "Unrecognized option: %c\n", option);
show_usage(argv[0]);
break;
}
}
/* We expect at least one argument after the options: The input
* file name.
*/
if (optind > argc - 1)
{
fprintf(stderr, "Expected input file name\n");
show_usage(argv[0]);
}
astInFileName = argv[optind];
/* Read the plane file now so that we can be certain that it is
* a valid plaine file.
*/
if (astLoadPlaneFile(astInFileName) != PLANE_SUCCESS)
{
exit(1);
}
/* Tk_Main creates a Tcl interpreter and calls Tcl_AppInit() then
* begins processing window events and interactive commands.
*/
Tk_Main(1, argv, Tcl_AppInit);
exit(0);
}
/* This function can be called at any time after initialization to
* execute a user specified script.
*/
int do_tcl_action(const char *script)
{
return Tcl_Eval(astInterp, script);
}
/* Tcl_AppInit is called from Tcl_Main() after the Tcl interpreter has
* been created and before the script file or interactive command loop
* is entered.
*/
int Tcl_AppInit(Tcl_Interp *interp)
{
int i;
/* Save the interpreter for later */
astInterp = interp;
/* Initialize the edit windows before starting the Tcl parser */
for (i = 0; i < NUM_PLANES; i++)
{
astInitGraphics(&windows[i]);
}
/* Tcl_Init() sets up the Tcl library factility */
if (Tcl_Init(interp) == TCL_ERROR)
{
return TCL_ERROR;
}
if (Tk_Init(interp) == TCL_ERROR)
{
return TCL_ERROR;
}
/* Define application-specific commands */
Tcl_CreateCommand(astInterp, "ast_seteditmode", astSetEditMode,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_position", astNewPosition,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_zoom", astNewZoom,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_edit", astNewEdit,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_attributes", astNewAttributes,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_addrectangle", astAddRectangle,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateCommand(astInterp, "ast_save", astSaveRectangles,
(ClientData)0, (Tcl_CmdDeleteProc *) NULL);
/* Initialize the Tcl parser */
if (Tcl_EvalFile(astInterp, "tcledit.tk") != TCL_OK)
{
fprintf(stderr, "%s\n", Tcl_GetVar(astInterp, "errorCode", 0));
fprintf(stderr, "%s\n", Tcl_GetVar(astInterp, "errorInfo", 0));
exit(1);
}
return TCL_OK;
}
void astFatalError(char *message, ...)
{
va_list args;
va_start(args, message);
vfprintf(stderr, message, args);
putc('\n', stderr);
va_end(args);
exit(1);
}