$ git clone http://gcodetool.ion.nu/gcodetool.git
commit a041b54c558d677af05beb61b64dcea6325b203b
Author: Alicia <...>
Date:   Sat Oct 26 16:03:51 2019 +0200

    Added center of mass calculation to the info subcommand and a -p argument to the preview subcommand to mark a point in space.

diff --git a/info.c b/info.c
index 27e700e..c4e7916 100644
--- a/info.c
+++ b/info.c
@@ -68,6 +68,7 @@ int gtool_info(const char* filename)
   unsigned int tooltempcount=0;
   double* bedtemps=0;
   unsigned int bedtempcount=0;
+  double centermass[4]={0,0,0,0};
   double eoffset=0;
   // Loop through gcode and analyze commands
   while((cmd=gcode_readcommand(gcode)))
@@ -84,14 +85,19 @@ int gtool_info(const char* filename)
       char move=(!isnan(xval) || !isnan(yval));
       if(!isnan(fval)){frate=fval;}
       if(!isnan(zval)){zpos=zval;} // Take things like 'lift head' into account, don't treat as Z until there's a positive E
-      // Printing line length
       if(!isnan(eval) && move && eval>0)
       {
+        // Printing line length
         double xdist=x.last-(isnan(xval)?x.last:xval);
         double ydist=y.last-(isnan(yval)?y.last:yval);
         double dist=sqrt(xdist*xdist+ydist*ydist);
 // printf("Print distance: %f\n", dist);
         minmax(&emove, dist);
+        // Center of mass
+        centermass[0]+=(isnan(xval)?x.last:(xval+x.last)/2)*(eval-e.last);
+        centermass[1]+=(isnan(yval)?y.last:(yval+y.last)/2)*(eval-e.last);
+        centermass[2]+=(isnan(zpos)?layer.last:zpos)*(eval-e.last);
+        centermass[3]+=eval-e.last;
         // Layer heights
         if(!isnan(zpos))
         {
@@ -168,7 +174,7 @@ int gtool_info(const char* filename)
     if(i==bedtempcount-1 && bedtemps[i]==0){break;}
     printf("%s%.*f°C", (i>0)?", ":"", pd(bedtemps[i]));
   }
-  if(i>0 && i==bedtempcount){printf(" (WARNING: This gcode file appears to leave to bed heater on)");}
+  if(i>0 && i==bedtempcount){printf(" (WARNING: This gcode file appears to leave the bed heater on)");}
   printf("\n");
   printf("Tool temperature(s): ");
   for(i=0; i<tooltempcount; ++i)
@@ -176,7 +182,7 @@ int gtool_info(const char* filename)
     if(i==tooltempcount-1 && tooltemps[i]==0){break;}
     printf("%s%.*f°C", (i>0)?", ":"", pd(tooltemps[i]));
   }
-  if(i>0 && i==tooltempcount){printf(" (WARNING: This gcode file appears to leave to tool heater on)");}
+  if(i>0 && i==tooltempcount){printf(" (WARNING: This gcode file appears to leave the tool heater on)");}
   printf("\n");
   printf("Size(X): %.*fmm (%.*f - %.*f)\n", pd(x.max-x.min), pd(x.min), pd(x.max));
   printf("Size(Y): %.*fmm (%.*f - %.*f)\n", pd(y.max-y.min), pd(y.min), pd(y.max));
@@ -187,6 +193,10 @@ int gtool_info(const char* filename)
   printf("Un-retraction length: %.*fmm\n", pd(retraction.max));
   printf("Number of retractions: %u\n", retractcount);
   printf("Filament use: %.*fmm\n", pd(e.max-e.min+eoffset));
+  centermass[0]/=centermass[3];
+  centermass[1]/=centermass[3];
+  centermass[2]/=centermass[3];
+  printf("Center of mass: %.*f x %.*f x %.*f\n", pd(centermass[0]), pd(centermass[1]), pd(centermass[2]));
   printf("Retraction speed: %.*fmm/s - %.*fmm/s\n", pd(retractspeed.min/60), pd(retractspeed.max/60));
   printf("Print speed: %.*fmm/s - %.*fmm/s\n", pd(printspeed.min/60), pd(printspeed.max/60));
   printf("Travel speed: %.*fmm/s - %.*fmm/s\n", pd(travelspeed.min/60), pd(travelspeed.max/60));
diff --git a/preview.c b/preview.c
index b59c37a..3274693 100644
--- a/preview.c
+++ b/preview.c
@@ -30,6 +30,7 @@ struct line // TODO: Consider non-planar printing?
 {
   double x[2];
   double y[2];
+  char special;
 };
 
 struct vectorlayer
@@ -46,18 +47,22 @@ struct vectors
   unsigned int current;
 };
 
-static void vector_draw(struct vectors* v, double startx, double starty, double endx, double endy, double z)
+static void vector_draw(struct vectors* v, double startx, double starty, double endx, double endy, double z, char special)
 {
   if(v->layers && v->layers[v->current].z!=z)
   {
     for(v->current=0; v->current<v->layercount; ++v->current)
     {
-      if(v->layers[v->current].z==z){break;}
+      if(v->layers[v->current].z>=z){break;}
     }
   }
-  if(v->current==v->layercount)
+  if(v->current==v->layercount || v->layers[v->current].z!=z)
   {
     v->layers=realloc(v->layers, sizeof(struct vectorlayer)*(++v->layercount));
+    if(v->current+1<v->layercount) // Insert a layer rather than adding one on top
+    {
+      memmove(&v->layers[v->current+1], &v->layers[v->current], (v->layercount-v->current-1)*sizeof(struct vectorlayer));
+    }
     v->layers[v->current].z=z;
     v->layers[v->current].lines=0;
     v->layers[v->current].linecount=0;
@@ -69,6 +74,7 @@ static void vector_draw(struct vectors* v, double startx, double starty, double
   l->x[1]=endx;
   l->y[0]=starty;
   l->y[1]=endy;
+  l->special=special;
 }
 
 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
@@ -105,7 +111,7 @@ static void cb_drawline(struct img* img, int* x, int* y, unsigned char r, unsign
   }
 }
 
-int gtool_preview(const char* filename, struct img* img, double angle, double zrot)
+int gtool_preview(const char* filename, struct img* img, double angle, double zrot, double* point)
 {
   struct gcode* gcode=gcode_read(filename);
   if(!gcode){return -1;}
@@ -146,7 +152,7 @@ int gtool_preview(const char* filename, struct img* img, double angle, double zr
       if(!isnan(eval) && eval>olde && move)
       {
         olde=eval;
-        vector_draw(&vectors, oldx, oldy, xval, yval, zval);
+        vector_draw(&vectors, oldx, oldy, xval, yval, zval, 0);
         if(isnan(max[0]) || xval>max[0]){max[0]=xval;}
         if(isnan(max[1]) || yval>max[1]){max[1]=yval;}
         if(isnan(max[2]) || zval>max[2]){max[2]=zval;}
@@ -166,6 +172,17 @@ 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};
+  if(!isnan(point[0]+point[1]+point[2])) // Draw cross to mark the point
+  {
+    vector_draw(&vectors, min[0]-5, point[1], max[0]+5, point[1], point[2], 1);
+    vector_draw(&vectors, point[0], min[1]-5, point[0], max[1]+5, point[2], 1);
+    double z;
+    for(z=min[2]-5; z<max[2]+5; z+=0.1)
+    {
+      vector_draw(&vectors, point[0]-0.2, point[1], point[0]+0.2, point[1], z, 1);
+      vector_draw(&vectors, point[0], point[1]-0.2, point[0], point[1]+0.2, z, 1);
+    }
+  }
   // Figure out how to scale things
   double scale=0;
   unsigned int i;
@@ -198,10 +215,12 @@ if(fabs(angle)>M_PI_2){i=vectors.layercount-1-i;}
 // TODO: Do this more cleanly
 struct line line;
 struct line* l=&line;
+// Rotate X and Y around Z
 l->y[0]=sin(zrot)*cy(oldl->y[0])+cos(zrot)*cx(oldl->x[0]);
 l->x[0]=sin(zrot+M_PI_2)*cy(oldl->y[0])+cos(zrot+M_PI_2)*cx(oldl->x[0]);
 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]);
+// Rotate the resulting Y around X
 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)) // Polarity comparison
@@ -210,7 +229,12 @@ l->y[1]=l->y[1]*cos(angle)+(vectors.layers[i].z-center[2])*scale*sin(angle);
         if(fabs(l->y[0])>img->height/2 && fabs(l->y[1])>img->height/2 && polcmp(l->y)){continue;}
         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(oldl->special)
+        {
+          img->drawline(img, x, y, 255, 0, 0, linescale);
+        }else{
+          img->drawline(img, x, y, c, c/1.05, c/2, linescale);
+        }
       }
       if(fabs(angle)>M_PI_2){i=vectors.layercount-1-i;}
     }
@@ -243,6 +267,7 @@ int gtool_preview_arg(int argc, char** argv)
   const char* outfile=0;
   double rx=0,rz=0;
   int width=0,height=0;
+  double point[3]={NAN,NAN,NAN};
   struct img*(*imginit)(int width, int height, const char* filename)=0;
   int i;
   for(i=2; i<argc; ++i)
@@ -282,6 +307,19 @@ int gtool_preview_arg(int argc, char** argv)
       width=strtol(argv[++i], &end, 0);
       if(end && end[0]){height=strtol(&end[1], 0, 0);}else{height=width;}
     }
+    else if((!strcmp(argv[i], "-p") || !strcmp(argv[i], "--point")) && i+1<argc)
+    {
+      ++i;
+      char* y=strchr(argv[i], 'x');
+      if(!y){continue;} // TODO: Warn about invalid coordinate format
+      while(strchr("x, ",y[0])){y=&y[1];}
+      char* z=strchr(y, 'x');
+      if(!z){continue;}
+      while(strchr("x, ",z[0])){z=&z[1];}
+      point[0]=strtod(argv[i],0);
+      point[1]=strtod(y,0);
+      point[2]=strtod(z,0);
+    }
     else if(!filename)
     {
       filename=argv[i];
@@ -295,6 +333,7 @@ int gtool_preview_arg(int argc, char** argv)
            "--r[xz] <angle>             = rotate X or Z <angle> degrees\n"
            "-o/--output <method> <file> = select output method and filename\n"
            "-s/--size <width>x<height>  = set output size\n"
+           "-p/--point <X>x<Y>x<Z>      = point at a point in space\n"
            "TODO: scale etc.\n"
            "\n"
            "Available output methods:\n"
@@ -323,5 +362,5 @@ int gtool_preview_arg(int argc, char** argv)
     img=gtool_preview_pnm(width, height, "preview.pnm"); // Last resort
   }
   if(!img->drawline){img->drawline=cb_drawline;}
-  return gtool_preview(filename, img, rx, rz);
+  return gtool_preview(filename, img, rx, rz, point);
 }