$ git clone http://gcodetool.ion.nu/gcodetool.git
commit 6015be093b34dafc0bdafdae4c3197c2e8820339
Author: Alicia <...>
Date: Thu Oct 17 22:49:26 2019 +0200
Added an SDL output method to gcodetool preview.
diff --git a/Makefile b/Makefile
index cc05f2c..a3565c3 100644
--- a/Makefile
+++ b/Makefile
@@ -2,11 +2,22 @@ PREFIX=/usr
BINDIR=$(PREFIX)/bin
SHAREDIR=$(PREFIX)/share
CFLAGS=-Wall -g3
-gcodetool: main.o gcode.o mirror.o move.o info.o preview.o preview_term.o preview_pnm.o
- $(CC) $^ -lm -o $@
+OBJECTS=main.o gcode.o mirror.o move.o info.o preview.o preview_term.o preview_pnm.o
+ifneq ($(shell pkg-config $(PCFLAGS) --libs sdl2 2> /dev/null),)
+ LIBS+=$(shell pkg-config $(PCFLAGS) --libs sdl2)
+ CFLAGS+=$(shell pkg-config $(PCFLAGS) --cflags sdl2) -DHAS_SDL=1
+ OBJECTS+=preview_sdl.o
+endif
+
+gcodetool: $(OBJECTS)
+ $(CC) $^ $(LIBS) -lm -o $@
static: CC+=-static
+static: PCFLAGS+=--static
static: gcodetool
install: gcodetool
install -D gcodetool $(BINDIR)/gcodetool
install -D gcodetool.thumbnailer $(SHAREDIR)/thumbnailers/gcodetool.thumbnailer
+
+clean:
+ rm -f $(OBJECTS) gcodetool
diff --git a/main.c b/main.c
index e307c5c..82310cb 100644
--- a/main.c
+++ b/main.c
@@ -55,11 +55,11 @@ int main(int argc, char** argv)
"mirror[XYZ]\n"
"move[XYZ]\n"
"info\n"
+ "preview\n"
"\nTODO:\n"
"rotate[XYZ]\n"
"speed (change speeds for selectable cases like travel, extrude)\n"
"remove (exclude parts of the object)\n"
-"preview, somehow\n"
"scale (will also need to scale E)\n", argv[0]);
#ifdef WIN32 // Foolproofing
if(argc==1)
diff --git a/preview.c b/preview.c
index 90b22ea..b59c37a 100644
--- a/preview.c
+++ b/preview.c
@@ -24,6 +24,7 @@
#include "preview.h"
extern struct img* gtool_preview_term(int width, int height, const char* filename);
extern struct img* gtool_preview_pnm(int width, int height, const char* filename);
+extern struct img* gtool_preview_sdl(int width, int height, const char* filename);
struct line // TODO: Consider non-planar printing?
{
@@ -70,6 +71,40 @@ static void vector_draw(struct vectors* v, double startx, double starty, double
l->y[1]=endy;
}
+static void cb_drawline(struct img* img, int* x, int* y, unsigned char r, unsigned char g, unsigned char b, double linewidth) // Generic line drawing implementation for display methods that don't have their own line drawing functions
+{
+ double stepx;
+ double stepy;
+ double steps;
+ // Figure out whether to go x/y or y/x
+ if(fabs(x[0]-x[1])>fabs(y[0]-y[1]))
+ {
+ steps=fabs(x[0]-x[1]);
+ stepx=(x[1]>x[0])?1:-1;
+ stepy=(y[1]-y[0])/steps;
+ }else{
+ steps=fabs(y[0]-y[1]);
+ stepy=(y[1]>y[0])?1:-1;
+ stepx=(x[1]-x[0])/steps;
+ }
+ // Expand the line perpendicular to the line's angle
+ double lineangle=atan2(stepx,stepy);
+ double linestepx=sin(lineangle+M_PI_2);
+ double linestepy=cos(lineangle+M_PI_2);
+ int m;
+ for(m=0; m<linewidth; ++m)
+ {
+ double k;
+ for(k=0; k<steps; ++k)
+ {
+ unsigned int px=x[0]+stepx*k+linestepx*(m-linewidth/2);
+ unsigned int py=y[0]+stepy*k+linestepy*(m-linewidth/2);
+ if(px>=img->width || py>=img->height){continue;}
+ img->drawpixel(img, px, py, r, g, b);
+ }
+ }
+}
+
int gtool_preview(const char* filename, struct img* img, double angle, double zrot)
{
struct gcode* gcode=gcode_read(filename);
@@ -83,11 +118,21 @@ int gtool_preview(const char* filename, struct img* img, double angle, double zr
struct vectors vectors={0,0,0};
double max[]={NAN,NAN,NAN};
double min[]={NAN,NAN,NAN};
+// int bary[]={img->height/2,img->height/2};
+// int bari=0; // Part of experimental progress bar below
// Loop through gcode and analyze commands
while((cmd=gcode_readcommand(gcode)))
{
// Show progress if outputting to a tty
if(tty){printf("%.2f%%\r", (double)gcode->pos*100/(double)gcode->size);}
+/*
+ if(!((++bari)%1000)) // Experimental graphical progress bar. Doesn't work well across display methods
+ {
+ int barx[]={0,((double)gcode->pos*(double)img->width)/gcode->size};
+ img->drawline(img, barx, bary, 0,255,0, img->height/5);
+ img->display(img);
+ }
+*/
if(cmd->key=='G' && cmd->num<=1)
{
double xval=gcommand_getparam(cmd, 'X');
@@ -122,7 +167,7 @@ int gtool_preview(const char* filename, struct img* img, double angle, double zr
gcode_free(gcode);
double center[]={(min[0]+max[0])/2, (min[1]+max[1])/2, (min[2]+max[2])/2};
// Figure out how to scale things
- double scale=0; // max[0];
+ double scale=0;
unsigned int i;
for(i=0; i<3; ++i)
{
@@ -131,7 +176,7 @@ int gtool_preview(const char* filename, struct img* img, double angle, double zr
}
scale=(img->width>img->height)?(img->height/scale):(img->width/scale);
scale*=sin(45);
-// TODO: Intermediate bitmap for shading? (plus then the final bitmap can be navigated with less re-processing. but that would be pixely)
+// TODO: Intermediate bitmap for shading? (plus then the final bitmap can be navigated with less re-processing. but that could be pixely)
while(1)
{
if(fabs(angle)>=M_PI){angle=(angle>0)?-M_PI:M_PI;}
@@ -159,41 +204,14 @@ l->y[1]=sin(zrot)*cy(oldl->y[1])+cos(zrot)*cx(oldl->x[1]);
l->x[1]=sin(zrot+M_PI_2)*cy(oldl->y[1])+cos(zrot+M_PI_2)*cx(oldl->x[1]);
l->y[0]=l->y[0]*cos(angle)+(vectors.layers[i].z-center[2])*scale*sin(angle);
l->y[1]=l->y[1]*cos(angle)+(vectors.layers[i].z-center[2])*scale*sin(angle);
-#define polcmp(n) ((n[0]>=0&&n[1]>=0) || (n[0]<0&&n[1]<0))
+#define polcmp(n) ((n[0]>=0&&n[1]>=0) || (n[0]<0&&n[1]<0)) // Polarity comparison
// Skip lines that are just out of view (saves resources when zoomed in far)
if(fabs(l->x[0])>img->width/2 && fabs(l->x[1])>img->width/2 && polcmp(l->x)){continue;}
if(fabs(l->y[0])>img->height/2 && fabs(l->y[1])>img->height/2 && polcmp(l->y)){continue;}
- double stepx;
- double stepy;
- double steps;
- // Figure out whether to go x/y or y/x
- if(fabs(l->x[0]-l->x[1])>fabs(l->y[0]-l->y[1]))
- {
- steps=fabs(l->x[0]-l->x[1]);
- stepx=(l->x[1]>l->x[0])?1:-1;
- stepy=(l->y[1]-l->y[0])/steps;
- }else{
- steps=fabs(l->y[0]-l->y[1]);
- stepy=(l->y[1]>l->y[0])?1:-1;
- stepx=(l->x[1]-l->x[0])/steps;
- }
-// Expand the line perpendicular to the line's angle
-double lineangle=atan2(stepx,stepy);
-double linestepx=sin(lineangle+M_PI_2);
-double linestepy=cos(lineangle+M_PI_2);
-int m;
-for(m=0; m<linescale; ++m)
-{
- double k;
- for(k=0; k<steps; ++k)
- {
- unsigned int x=(l->x[0]+img->width/2)+stepx*k+linestepx*(m-linescale/2);
- unsigned int y=(l->y[0]+img->height/2)+stepy*k+linestepy*(m-linescale/2);
- if(x>=img->width || y>=img->height){continue;}
- img->drawpixel(img, x, y, c, c/1.05, c/2);
- }
+ int x[]={l->x[0]+img->width/2, l->x[1]+img->width/2};
+ int y[]={l->y[0]+img->height/2, l->y[1]+img->height/2};
+ img->drawline(img, x, y, c, c/1.05, c/2, linescale);
}
-}
if(fabs(angle)>M_PI_2){i=vectors.layercount-1-i;}
}
img->display(img);
@@ -244,8 +262,17 @@ int gtool_preview_arg(int argc, char** argv)
}
else if((!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) && i+2<argc)
{
- if(!strcmp(argv[i+1], "term")){imginit=gtool_preview_term;}
- else if(!strcmp(argv[i+1], "pnm")){imginit=gtool_preview_pnm;}
+ if(!strcmp(argv[i+1], "pnm")){imginit=gtool_preview_pnm;}
+#ifndef WIN32
+ else if(!strcmp(argv[i+1], "term")){imginit=gtool_preview_term;}
+#endif
+#if HAS_SDL
+ else if(!strcmp(argv[i+1], "sdl")){imginit=gtool_preview_sdl;}
+#endif
+ else{
+ printf("Unknown output method '%s'\n", argv[i+1]);
+ return 1;
+ }
i+=2;
outfile=argv[i];
}
@@ -271,14 +298,30 @@ int gtool_preview_arg(int argc, char** argv)
"TODO: scale etc.\n"
"\n"
"Available output methods:\n"
+#ifndef WIN32
"term (display using ANSI escape codes, use arrows and +/- to navigate)\n"
+#endif
"pnm\n"
+#ifdef HAS_SDL
+ "sdl (use arrows and +/- to navigate)\n"
+#endif
,argv[0], argv[1]);
return -1;
}
if(!strncmp(filename, "file://", 7)){filename=&filename[7];}
-// TODO: Mechanism for selecting method by what's available. Not relevant until there's another direct display method
- if(!imginit){imginit=gtool_preview_term;}
- struct img* img=imginit(width,height,outfile);
+ struct img* img;
+ if(imginit)
+ {
+ img=imginit(width,height,outfile);
+ }else{ // Select a method by what's available
+#ifdef HAS_SDL
+ if(!(img=gtool_preview_sdl(width,height,0)))
+#endif
+#ifndef WIN32
+ if(!(img=gtool_preview_term(width,height,0)))
+#endif
+ img=gtool_preview_pnm(width, height, "preview.pnm"); // Last resort
+ }
+ if(!img->drawline){img->drawline=cb_drawline;}
return gtool_preview(filename, img, rx, rz);
}
diff --git a/preview.h b/preview.h
index 4e664eb..57874bc 100644
--- a/preview.h
+++ b/preview.h
@@ -33,6 +33,7 @@ struct img
int width;
int height;
void(*drawpixel)(struct img*,int x, int y, unsigned char r, unsigned char g, unsigned char b);
+ void(*drawline)(struct img*,int* x, int* y, unsigned char r, unsigned char g, unsigned char b, double linewidth);
void(*clear)(struct img*);
void(*display)(struct img*);
enum previewcontrol(*input)(struct img*);
diff --git a/preview_pnm.c b/preview_pnm.c
index 0893ffa..bb6297d 100644
--- a/preview_pnm.c
+++ b/preview_pnm.c
@@ -70,6 +70,7 @@ static void cb_clean(struct img* imgp)
struct img* gtool_preview_pnm(int width, int height, const char* filename)
{
struct img_pnm* img=malloc(sizeof(struct img_pnm));
+ memset(img, 0, sizeof(struct img_pnm));
if(filename && strcmp(filename, "-"))
{
img->out=fopen(filename, "w");
@@ -78,11 +79,8 @@ struct img* gtool_preview_pnm(int width, int height, const char* filename)
img->out=stdout;
img->bin=0;
}
- if(!width || !height) // Make up sizes if not specified
- {
- if(!width){width=1024;}
- if(!height){height=1024;}
- }
+ if(!width){width=1024;} // Make up sizes if not specified
+ if(!height){height=1024;}
// Allocate buffer
img->img.width=width;
img->img.height=height;
diff --git a/preview_sdl.c b/preview_sdl.c
new file mode 100644
index 0000000..24b7ea8
--- /dev/null
+++ b/preview_sdl.c
@@ -0,0 +1,114 @@
+/*
+ gcodetool, a utility for analyzing/modifying already sliced parts
+ Copyright (C) 2019 alicia@ion.nu
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <SDL.h>
+#include "preview.h"
+
+struct img_sdl
+{
+ struct img img;
+ SDL_Window* win;
+ SDL_Renderer* renderer;
+};
+
+static void cb_drawline(struct img* imgp,int* x, int* y, unsigned char r, unsigned char g, unsigned char b, double linewidth)
+{
+ struct img_sdl* img=(struct img_sdl*)imgp;
+ SDL_SetRenderDrawColor(img->renderer, r, g, b, SDL_ALPHA_OPAQUE);
+ double lineangle=atan2(x[0]-x[1],y[0]-y[1]);
+ double linestepx=sin(lineangle+M_PI_2);
+ double linestepy=cos(lineangle+M_PI_2);
+ int m;
+ for(m=0; m<linewidth; ++m)
+ {
+ int xs[]={x[0]+linestepx*(m-linewidth/2),
+ x[1]+linestepx*(m-linewidth/2)};
+ int ys[]={y[0]+linestepy*(m-linewidth/2),
+ y[1]+linestepy*(m-linewidth/2)};
+ SDL_RenderDrawLine(img->renderer, xs[0], ys[0], xs[1], ys[1]);
+ }
+}
+
+static void cb_clear(struct img* imgp)
+{
+ struct img_sdl* img=(struct img_sdl*)imgp;
+ SDL_SetRenderDrawColor(img->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ SDL_RenderClear(img->renderer);
+}
+
+static void cb_display(struct img* imgp)
+{
+ struct img_sdl* img=(struct img_sdl*)imgp;
+ SDL_RenderPresent(img->renderer);
+}
+
+static void cb_clean(struct img* imgp)
+{
+ struct img_sdl* img=(struct img_sdl*)imgp;
+ SDL_DestroyWindow(img->win);
+ SDL_DestroyRenderer(img->renderer);
+ free(img);
+}
+
+static enum previewcontrol cb_input(struct img* imgp)
+{
+ enum previewcontrol ret=PREVIEW_NOINPUT;
+ SDL_Event e;
+ while(SDL_PollEvent(&e))
+ {
+ if(e.type==SDL_QUIT){return PREVIEW_QUIT;}
+ if(e.type==SDL_KEYDOWN)
+ {
+ if(e.key.keysym.sym==SDLK_RIGHT){ret=PREVIEW_ZRP;}
+ else if(e.key.keysym.sym==SDLK_LEFT){ret=PREVIEW_ZRM;}
+ else if(e.key.keysym.sym==SDLK_UP){ret=PREVIEW_XRP;}
+ else if(e.key.keysym.sym==SDLK_DOWN){ret=PREVIEW_XRM;}
+ else if(e.key.keysym.sym==SDLK_PLUS || e.key.keysym.sym==SDLK_KP_PLUS){ret=PREVIEW_ZOOMP;}
+ else if(e.key.keysym.sym==SDLK_MINUS || e.key.keysym.sym==SDLK_KP_MINUS){ret=PREVIEW_ZOOMM;}
+ else if(e.key.keysym.sym==SDLK_q){return PREVIEW_QUIT;}
+ }
+ }
+ SDL_Delay(10);
+ return ret;
+}
+
+struct img* gtool_preview_sdl(int width, int height, const char* filename)
+{
+ SDL_Init(SDL_INIT_VIDEO);
+ struct img_sdl* img=malloc(sizeof(struct img_sdl));
+ memset(img, 0, sizeof(struct img_sdl));
+ if(!width){width=800;} // Make up sizes if not specified
+ if(!height){height=600;}
+ img->img.width=width;
+ img->img.height=height;
+ // Set up SDL window
+ img->win=SDL_CreateWindow("gcodetool preview", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
+ if(!img->win){free(img); return 0;}
+ img->renderer=SDL_CreateRenderer(img->win, -1, SDL_RENDERER_SOFTWARE); // Software-only is faster the way we're doing it
+ if(!img->renderer){SDL_DestroyWindow(img->win); free(img); return 0;}
+ SDL_RenderClear(img->renderer);
+ // Set callbacks
+ img->img.drawline=cb_drawline;
+ img->img.clear=cb_clear;
+ img->img.display=cb_display;
+ img->img.input=cb_input;
+ img->img.clean=cb_clean;
+ return (struct img*)img;
+}
diff --git a/preview_term.c b/preview_term.c
index c11f5d3..c491ea9 100644
--- a/preview_term.c
+++ b/preview_term.c
@@ -15,6 +15,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#ifndef WIN32
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -108,6 +109,7 @@ static void cb_clean(struct img* imgp)
struct img* gtool_preview_term(int width, int height, const char* filename)
{
struct img_term* img=malloc(sizeof(struct img_term));
+ memset(img, 0, sizeof(struct img_term));
if(filename && strcmp(filename, "-"))
{
img->fd=open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
@@ -142,3 +144,4 @@ struct img* gtool_preview_term(int width, int height, const char* filename)
img->img.clean=cb_clean;
return (struct img*)img;
}
+#endif