Toolbar drawing is working! CIRCLE syntax changed. There are issues in JoeyLib with drawing clipped circles.

This commit is contained in:
Scott Duensing 2022-12-11 20:41:05 -06:00
parent b0f3204748
commit 12b0d090a7
3 changed files with 309 additions and 103 deletions

View file

@ -89,6 +89,8 @@ void _jlDrawCircleClipped(jlContextT *c, jint16 x0, jint16 y0, jint16 radius) {
jint16 dy = 1;
jint16 err = dx - (jint16)(radius << 1);
//***TODO*** This produces negative Y values for large circles.
while (x >= y) {
if ((x0 + x < 320) && (y0 + y < 200)) jlDrawPixelSet(c, x0 + x, y0 + y);
if ((x0 + y < 320) && (y0 + x < 200)) jlDrawPixelSet(c, x0 + y, y0 + x);

View file

@ -450,10 +450,10 @@ int vecparser(char *programIn, VecByteCodeT *bytecode) {
break;
case PARSE_CIRCLE:
// Circle (value) at (value),(value)
if (!parserGetX(&tokenEnd, &variables, &y2)) break;
if (parserGetWord("AT", &tokenEnd) < 0) break;
// Circle (value),(value) radius (value)
if (!parserGetXY(&tokenEnd, &variables, &x1, &y1)) break;
if (parserGetWord("RADIUS", &tokenEnd) < 0) break;
if (!parserGetX(&tokenEnd, &variables, &y2)) break;
if (IS_NUMBER(x1)) if (x1 < 0 || x1 > 319) break;
if (IS_NUMBER(y1)) if (y1 < 0 || y1 > 199) break;
outputByte(bytecode, PARSE_CIRCLE);

View file

@ -47,6 +47,24 @@
#define PREVIEW_HEIGHT 400
enum ClickStateE {
CLICK_NONE = 0,
CLICK_BOX_1,
CLICK_BOX_2,
CLICK_CIRCLE_1,
CLICK_CIRCLE_2,
CLICK_ELLIPSE_1,
CLICK_ELLIPSE_2,
CLICK_FILL_1,
CLICK_LINE_1,
CLICK_LINE_2,
CLICK_LINE_X,
CLICK_PLOT_1,
CLICK_RECTANGLE_1,
CLICK_RECTANGLE_2
};
typedef enum ClickStateE ClickStateT;
typedef struct VectorDataS {
WindowDataT windowData;
GtkWidget *drawVectorImage;
@ -69,6 +87,9 @@ typedef struct VectorDataS {
char *buffer;
int bufferLength;
PointT **pointList;
gboolean allowRedraw;
ClickStateT clickState;
PointT clickTempPoint;
} VectorDataT;
@ -82,6 +103,8 @@ EVENT gboolean drawVectorImageMotionEvent(GtkWidget *widget, GdkEventMotion *ev
EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData);
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData);
static int getWord(VecByteCodeT *bytecode, int *index);
static void insertCommand(VectorDataT *self, char *command);
static void insertText(VectorDataT *self, char *text);
static void loadTraceImage(VectorDataT *self, char *filename);
EVENT void menuVectorEditCopy(GtkWidget *object, gpointer userData);
EVENT void menuVectorEditCut(GtkWidget *object, gpointer userData);
@ -96,13 +119,13 @@ EVENT void menuVectorHelpVector(GtkWidget *object, gpointer userData);
static void renderBytecode(VecByteCodeT *bytecode, VectorDataT *self);
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData);
static void setDirty(VectorDataT *self, gboolean dirty);
EVENT void toolBoxClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolCircleClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolEllipseClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolFillClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolLineClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolPlotClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolRectangleClicked(GtkToolButton *object, gpointer user_data);
EVENT void toolBoxClicked(GtkToolButton *object, gpointer userData);
EVENT void toolCircleClicked(GtkToolButton *object, gpointer userData);
EVENT void toolEllipseClicked(GtkToolButton *object, gpointer userData);
EVENT void toolFillClicked(GtkToolButton *object, gpointer userData);
EVENT void toolLineClicked(GtkToolButton *object, gpointer userData);
EVENT void toolPlotClicked(GtkToolButton *object, gpointer userData);
EVENT void toolRectangleClicked(GtkToolButton *object, gpointer userData);
static float variable(VectorDataT *self, unsigned char byte);
static int word(VectorDataT *self, unsigned short word);
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
@ -131,11 +154,104 @@ EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpoint
(void)object;
if (event->type == GDK_BUTTON_PRESS) {
// Clamp coordinates, just in case.
if (x < 0) x = 0;
if (x > 319) x = 319;
if (y < 0) y = 0;
if (y > 199) y = 199;
snprintf(temp, 8, "%d,%d", x, y);
switch (self->clickState) {
case CLICK_NONE:
// Do nothing.
break;
case CLICK_BOX_1:
insertText(self, temp);
insertText(self, " TO ");
self->clickState++;
break;
case CLICK_BOX_2:
insertText(self, temp);
self->clickState = CLICK_NONE;
break;
case CLICK_CIRCLE_1:
self->clickTempPoint.x = x;
self->clickTempPoint.y = y;
insertText(self, temp);
insertText(self, " RADIUS ");
self->clickState++;
break;
case CLICK_CIRCLE_2:
x = sqrt(pow(x - self->clickTempPoint.x, 2) + pow(y - self->clickTempPoint.y, 2));
snprintf(temp, 8, "%d", x);
insertText(self, temp);
self->clickState = CLICK_NONE;
break;
case CLICK_ELLIPSE_1:
insertText(self, temp);
insertText(self, " TO ");
self->clickState++;
break;
case CLICK_ELLIPSE_2:
insertText(self, temp);
self->clickState = CLICK_NONE;
break;
case CLICK_FILL_1:
insertText(self, temp);
self->clickState = CLICK_NONE;
break;
case CLICK_LINE_1:
insertText(self, temp);
insertText(self, " TO ");
self->clickState++;
break;
case CLICK_LINE_2:
insertText(self, temp);
self->clickState++;
break;
case CLICK_LINE_X:
insertText(self, " TO ");
insertText(self, temp);
break;
case CLICK_PLOT_1:
insertText(self, temp);
insertText(self, " TO ");
self->clickState = CLICK_NONE;
break;
case CLICK_RECTANGLE_1:
insertText(self, temp);
insertText(self, " TO ");
self->clickState++;
break;
case CLICK_RECTANGLE_2:
insertText(self, temp);
self->clickState = CLICK_NONE;
break;
}
}
/*
if (event->type == GDK_DOUBLE_BUTTON_PRESS) {
snprintf(temp, 8, "%d,%d", x, y);
SSM(SCI_ADDTEXT, strlen(temp), (sptr_t)temp);
SSM(SCI_GRABFOCUS, 0, 0);
}
*/
}
@ -196,41 +312,45 @@ EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer user
EVENT gboolean drawVectorImageMotionEvent(GtkWidget *widget, GdkEventMotion *event, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
int closest = -1;
int distance = INT_MAX;
int x = (int)(event->x * 0.5);
int y = (int)(event->y * 0.5);
int temp;
int i;
char buffer[8];
VectorDataT *self = (VectorDataT *)userData;
int closest = -1;
int distance = INT_MAX;
int x = (int)(event->x * 0.5);
int y = (int)(event->y * 0.5);
int temp;
int i;
char buffer[8];
// Find closest point to mouse pointer.
for (i=0; i<arrlen(self->pointList); i++) {
temp = sqrt(pow(x - self->pointList[i]->x, 2) + pow(y - self->pointList[i]->y, 2));
if (temp < distance) {
closest = i;
distance = temp;
// If we're in a drawing action, bail out of this.
if (self->clickState != CLICK_NONE) return FALSE;
// If they're holding the button down, update the point.
if (event->state & GDK_BUTTON1_MASK) {
// Find closest point to mouse pointer.
for (i = 0; i < arrlen(self->pointList); i++) {
temp = sqrt(pow(x - self->pointList[i]->x, 2) + pow(y - self->pointList[i]->y, 2));
if (temp < distance) {
closest = i;
distance = temp;
}
}
}
// Did we find a point to edit?
if (closest >= 0) {
// Select first occurrence of it in the text editor.
snprintf(buffer, 8, "%d,%d", self->pointList[closest]->x, self->pointList[closest]->y);
SSM(SCI_TARGETWHOLEDOCUMENT, 0, 0);
SSM(SCI_SETSEARCHFLAGS, SCFIND_NONE, 0);
temp = SSM(SCI_SEARCHINTARGET, strlen(buffer), (sptr_t)buffer);
// Did we find it in the text?
if (temp >= 0) {
// Select the text.
SSM(SCI_SETSEL, temp, temp + strlen(buffer));
// If they're holding the button down, update the point.
if (event->state & GDK_BUTTON1_MASK) {
// Did we find a point to edit?
if (closest >= 0) {
// Select first occurrence of it in the text editor.
snprintf(buffer, 8, "%d,%d", self->pointList[closest]->x, self->pointList[closest]->y);
SSM(SCI_TARGETWHOLEDOCUMENT, 0, 0);
SSM(SCI_SETSEARCHFLAGS, SCFIND_NONE, 0);
temp = SSM(SCI_SEARCHINTARGET, strlen(buffer), (sptr_t)buffer);
// Did we find it in the text?
if (temp >= 0) {
// Select the text.
SSM(SCI_SETSEL, temp, temp + strlen(buffer));
// Are these values sane?
if (x < 0) x = 0;
if (x < 0) x = 0;
if (x > 319) x = 319;
if (y < 0) y = 0;
if (y < 0) y = 0;
if (y > 199) y = 199;
snprintf(buffer, 8, "%d,%d", x, y);
SSM(SCI_REPLACETARGET, -1, (sptr_t)buffer);
@ -257,30 +377,35 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
switch (notifyData->nmhdr.code) {
case SCN_MODIFIED:
if (notifyData->modificationType & SC_MOD_INSERTTEXT || notifyData->modificationType & SC_MOD_DELETETEXT) {
// Allocate space to fetch code from editor.
utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, length);
// Clear error markers.
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0);
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0);
// Fetch code.
SSM(SCI_GETTEXT, length, (sptr_t)self->buffer);
// Parse code.
byteCode.bytes = NULL;
byteCode.length = 0;
byteCode.bufferSize = 0;
lineNumber = vecparser(self->buffer, &byteCode);
if (lineNumber >= 0) {
// Mark lines that fail to parse.
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW);
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT);
} else {
// Cancel any in-progress drawing action.
self->clickState = CLICK_NONE;
// Is it safe to repaint now?
if (self->allowRedraw) {
// Allocate space to fetch code from editor.
utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, length);
// Clear error markers.
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0);
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0);
// Fetch code.
SSM(SCI_GETTEXT, length, (sptr_t)self->buffer);
// Parse code.
byteCode.bytes = NULL;
byteCode.length = 0;
byteCode.bufferSize = 0;
lineNumber = vecparser(self->buffer, &byteCode);
if (lineNumber >= 0) {
// Mark lines that fail to parse.
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_ARROW);
SSM(SCI_MARKERADD, lineNumber, MARKER_ERROR_HIGHLIGHT);
} else {
#ifdef DEBUG
FILE *out = fopen("bytecode.bin", "wb");
fwrite(byteCode.bytes, byteCode.length, 1, out);
fclose(out);
FILE *out = fopen("bytecode.bin", "wb");
fwrite(byteCode.bytes, byteCode.length, 1, out);
fclose(out);
#endif
// All good!
renderBytecode(&byteCode, self);
// All good!
renderBytecode(&byteCode, self);
}
}
// Release bytecode.
@ -301,6 +426,19 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
}
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
char *temp = NULL;
debug("fileVectorTraceImageFileSet fired\n");
temp = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object));
loadTraceImage(self, temp);
DEL(temp);
setDirty(self, TRUE);
}
static int getWord(VecByteCodeT *bytecode, int *index) {
int word;
@ -313,16 +451,56 @@ static int getWord(VecByteCodeT *bytecode, int *index) {
}
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
char *temp = NULL;
static void insertCommand(VectorDataT *self, char *command) {
int pos;
int line;
int len;
char *newLine = "\n\0";
debug("fileVectorTraceImageFileSet fired\n");
temp = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object));
loadTraceImage(self, temp);
DEL(temp);
// Position Scintilla cursor at the start of the current line.
pos = SSM(SCI_GETCURRENTPOS, 0, 0);
line = SSM(SCI_LINEFROMPOSITION, pos, 0);
SSM(SCI_GOTOLINE, line, 0);
setDirty(self, TRUE);
// I'm using 'buffer' and don't want a redraw until I'm done.
self->allowRedraw = FALSE;
// Collect leading whitespace on this line.
len = SSM(SCI_GETLINE, line, NULL);
utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, len + 1);
SSM(SCI_GETLINE, line, (sptr_t)self->buffer);
self->buffer[len + 1] = 0;
pos = 0;
while (self->buffer[pos] == ' ' || self->buffer[pos] == '\t') {
pos++;
if (pos > len - 1) {
pos = 0;
break;
}
}
self->buffer[pos] = 0;
// Insert CR on next line and leading whitespace of previous line.
line++;
SSM(SCI_GOTOLINE, line, 0);
insertText(self, "\n");
SSM(SCI_GOTOLINE, line, 0);
insertText(self, self->buffer);
insertText(self, command);
insertText(self, " ");
// Drawing is OK again.
self->allowRedraw = TRUE;
}
static void insertText(VectorDataT *self, char *text) {
int pos;
pos = SSM(SCI_GETCURRENTPOS, 0, 0);
SSM(SCI_INSERTTEXT, -1, (sptr_t)text);
pos += strlen(text);
SSM(SCI_GOTOPOS, pos, 0);
}
@ -434,31 +612,33 @@ EVENT void menuVectorFileNew(GtkWidget *object, gpointer userData) {
(void)object;
if (self->windowData.isDirty == TRUE) {
if (utilQuestionDialog(self->windowData.window, "New", "You have unsaved changes. Start new?")) {
// Clear editor.
SSM(SCI_CLEARALL, 0, 0);
// Clear error markers.
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0);
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0);
// Reset JoeyLib drawing state.
jlPaletteDefault(self->jlc);
jlDrawColorSet(self->jlc, 0);
jlDrawClear(self->jlc);
jlDrawColorSet(self->jlc, 15);
// Destroy trace image, if any.
if (self->trace != NULL) {
cairo_surface_destroy(self->trace);
self->trace = NULL;
gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->fileVectorTraceImage));
}
// Clear any filename.
if (self->filename != NULL) DEL(self->filename);
// Refresh widget.
gtk_widget_queue_draw(self->drawVectorImage);
// Mark clean.
setDirty(self, FALSE);
if (!utilQuestionDialog(self->windowData.window, "New", "You have unsaved changes. Start new?")) {
return;
}
}
// Clear editor.
SSM(SCI_CLEARALL, 0, 0);
// Clear error markers.
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_ARROW, 0);
SSM(SCI_MARKERDELETEALL, MARKER_ERROR_HIGHLIGHT, 0);
// Reset JoeyLib drawing state.
jlPaletteDefault(self->jlc);
jlDrawColorSet(self->jlc, 0);
jlDrawClear(self->jlc);
jlDrawColorSet(self->jlc, 15);
// Destroy trace image, if any.
if (self->trace != NULL) {
cairo_surface_destroy(self->trace);
self->trace = NULL;
gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->fileVectorTraceImage));
}
// Clear any filename.
if (self->filename != NULL) DEL(self->filename);
// Refresh widget.
gtk_widget_queue_draw(self->drawVectorImage);
// Mark clean.
setDirty(self, FALSE);
}
@ -488,13 +668,13 @@ EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData) {
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
if (self->filename != NULL) DEL(self->filename);
self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
// Use our New code to reset the editor state before we load.
setDirty(self, FALSE);
menuVectorFileNew(object, userData);
if (self->filename != NULL) DEL(self->filename);
self->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
in = fopen(self->filename, "rt");
if (in != NULL) {
self->buffer[0] = 0;
@ -523,6 +703,7 @@ EVENT void menuVectorFileOpen(GtkWidget *object, gpointer userData) {
fclose(in);
if (line != NULL) DEL(line);
SSM(SCI_ADDTEXT, strlen(self->buffer), (sptr_t)self->buffer);
//SSM(SCI_CONVERTEOLS, SC_EOL_CR, 0);
setDirty(self, FALSE); // Do again - loading text marks us dirty.
} else {
//***TODO*** Something bad happened.
@ -980,38 +1161,59 @@ static void setDirty(VectorDataT *self, gboolean dirty) {
}
EVENT void toolBoxClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolBoxClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "BOX");
self->clickState = CLICK_BOX_1;
}
EVENT void toolCircleClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolCircleClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "CIRCLE");
self->clickState = CLICK_CIRCLE_1;
}
EVENT void toolEllipseClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolEllipseClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "ELLIPSE");
self->clickState = CLICK_ELLIPSE_1;
}
EVENT void toolFillClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolFillClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "FILL");
self->clickState = CLICK_FILL_1;
}
EVENT void toolLineClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolLineClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "LINE");
self->clickState = CLICK_LINE_1;
}
EVENT void toolPlotClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolPlotClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "PLOT");
self->clickState = CLICK_PLOT_1;
}
EVENT void toolRectangleClicked(GtkToolButton *object, gpointer user_data) {
EVENT void toolRectangleClicked(GtkToolButton *object, gpointer userData) {
VectorDataT *self = (VectorDataT *)userData;
insertCommand(self, "RECTANGLE");
self->clickState = CLICK_RECTANGLE_1;
}
@ -1090,6 +1292,7 @@ void winVectorCreate(void) {
// Set up instance data.
self = NEW(VectorDataT);
self->windowData.closeWindow = winVectorClose;
self->allowRedraw = TRUE;
// Set up working buffer.
utilEnsureBufferSize((unsigned char **)&self->buffer, &self->bufferLength, 1024);
@ -1134,6 +1337,7 @@ void winVectorCreate(void) {
SSM(SCI_STYLESETBACK, STYLE_DEFAULT, 0);
SSM(SCI_STYLECLEARALL, 0, 0);
SSM(SCI_SETTABWIDTH, 3, 0);
//SSM(SCI_SETEOLMODE, SC_EOL_CR, 0);
SSM(SCI_SETMARGINWIDTHN, 0, (int)SSM(SCI_TEXTWIDTH, STYLE_LINENUMBER, (sptr_t)"_99999"));
SSM(SCI_SETMARGINWIDTHN, 1, 16);
SSM(SCI_SETWRAPMODE, SC_WRAP_NONE, 0);