Package Filters

Source Code of Filters.Filter_GeneratorTSP

package Filters;


import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.text.DecimalFormat;

import Makelangelo.MachineConfiguration;
import Makelangelo.Makelangelo;
import Makelangelo.Point2D;


/**
* Generate a Gcode file from the BufferedImage supplied.<br>
* Use the filename given in the constructor as a basis for the gcode filename, but change the extension to .ngc
* @author Dan
*/
public class Filter_GeneratorTSP extends Filter {
  public String GetName() { return "Big zig zag"; }
 
  // processing tools
  long t_elapsed,t_start;
  double progress;
  double old_len,len;
  long time_limit=10*60*1000// 10 minutes

  int numPoints;
  Point2D[] points = null;
  int[] solution = null;
  int scount;
 

  public String formatTime(long millis) {
      String elapsed="";
      long s=millis/1000;
      long m=s/60;
      long h=m/60;
      m%=60;
      s%=60;
      if(h>0) elapsed+=h+"h";
      if(h>0||m>0) elapsed+=m+"m";
      elapsed+=s+"s ";
      return elapsed;
  }
 

  public void UpdateProgress(double len,int color) {
    t_elapsed=System.currentTimeMillis()-t_start;
    double new_progress = 100.0 * (double)t_elapsed / (double)time_limit;
    if( new_progress > progress + 0.1 ) {
      // find the new tour length
      len=GetTourLength(solution);
      if( old_len > len ) {
        old_len=len;
        DecimalFormat flen=new DecimalFormat("#.##");
        String c;
        switch(color) {
        case  0: c="yellow"break;
        case  1: c="blue";    break;
        case  2: c="red";    break;
        default: c="white";   break;
        }
        Makelangelo.getSingleton().Log("<font color='"+c+"'>"+formatTime(t_elapsed)+": "+flen.format(len)+"mm</font>\n");
      }
      progress = new_progress;
      pm.setProgress((int)progress);
    }
  }
   
/*
  // does moving a point to somewhere else in the series shorten the series?
  // @TODO: Debug, it doesn't seem to work.
  // we have s1,s2,s3...e-1,e
  // check if s1,s3,...e-1,s2,e is shorter
  private int transposeForwardTest() {
    int start, end, j, once=0;
   
    for(start=0;start<numPoints-4 && !isCancelled();++start) {
      float a=CalculateWeight(solution[start],solution[start+1]);  // s1->s2
      float b=CalculateWeight(solution[start+1],solution[start+2]);  // s2->s3
      float d=CalculateWeight(solution[start],solution[start+2]);  // s1->s3
      // a+b > d always true, the line gets shorter at this end
      //assert(a+b>d);
      float abd=a+b-d;

      int best_end=-1;
      double best_diff=0;
     
      for(end=start+4;end<numPoints && !isCancelled();++end) {
        // before
        float c=CalculateWeight(solution[end-1],solution[end]);  // e-1->e
        // after
        float e=CalculateWeight(solution[end-1],solution[start+1]);  // e-1->s2
        float f=CalculateWeight(solution[start+1],solution[end]);  // s2->e
        // e+f > c always true, the line gets longer at this end
        //assert(e+f>c);
        float efc = e+f-c;

        float temp_diff = abd-efc;
        if( best_diff < temp_diff ) {
          best_diff = temp_diff;
          best_end=end;
          //DrawbotGUI.getSingleton().Log("<font color='red'>"+best_diff+"</font>\n");
        }
      }
     
      if(best_end != -1 && !isCancelled()) {
        once = 1;
        // move start+1 to just before end
        int temp=solution[start+1];
        for(j=start+1;j<best_end-1;++j) {
          solution[j]=solution[j+1];
        }
        solution[best_end-1]=temp;

        UpdateProgress(len,0);
      }
    }
    return once;
  }*/
/*
  // does moving a point to somewhere else in the series shorten the series?
  // @TODO: Debug, it doesn't seem to work.
  // we have s1,s2...e-2,e-1,e
  // check if s1,e-1,s2...e-2,e is shorter
  private int transposeBackwardTest() {
    int start, end, j, once=0;
   
    for(start=0;start<numPoints-4 && !isCancelled();++start) {
      float a=CalculateWeight(solution[start],solution[start+1]);  // s1->s2

      int best_end=-1;
      double best_diff=0;
     
      for(end=start+4;end<numPoints && !isCancelled();++end) {
        float b=CalculateWeight(solution[end-2],solution[end-1]);  // e2->e1
        float c=CalculateWeight(solution[end-1],solution[end]);  // e1->e
        float f=CalculateWeight(solution[end-2],solution[end]);  // e2->e
        // b+c > f, this end is getting shorter
        assert(b+c>f);
        float bcf=b+c-f;
       
        float d=CalculateWeight(solution[start],solution[end-1]);  // s1->e1
        float e=CalculateWeight(solution[end-1],solution[start+1]);  // e1->s2
        // d+e>a, this end is getting longer
        assert(d+e>a);
        float dea=d+e-a;

        float temp_diff = bcf-dea;
        if( best_diff < temp_diff ) {
          best_diff = temp_diff;
          best_end=end;
        }
      }
     
      if(best_end != -1 && !isCancelled()) {
        once = 1;
        // move best_end-1 to just after start
        int temp=solution[best_end-1];
        for(j=best_end-1;j>start+1;--j) {
          solution[j]=solution[j-1];
        }
        solution[start+1]=temp;

        UpdateProgress(len,3);
      }
    }
    return once;
  }*/

  // we have s1,s2...e-1,e
  // check if s1,e-1,...s2,e is shorter
  public int flipTests() {
    int start, end, j, once=0;
   
    for(start=0;start<numPoints-2 && !parent.isCancelled() && !pm.isCanceled();++start) {
      float a=CalculateWeight(solution[start],solution[start+1]);
      int best_end=-1;
      double best_diff=0;
     
      for(end=start+2;end<=numPoints && !parent.isCancelled() && !pm.isCanceled();++end) {
        // before
        float b=CalculateWeight(solution[end  ],solution[end  -1]);
        // after
        float c=CalculateWeight(solution[start],solution[end  -1]);
        float d=CalculateWeight(solution[end  ],solution[start+1]);
       
        double temp_diff=(a+b)-(c+d);
        if(best_diff < temp_diff) {
          best_diff = temp_diff;
          best_end=end;
        }
      }
     
      if(best_end != -1 && !parent.isCancelled() && !pm.isCanceled()) {
        once = 1;
        // do the flip
        int begin=start+1;
        int finish=best_end;
        int half=(finish-begin)/2;
        int temp;
        //DrawbotGUI.getSingleton().Log("<font color='red'>flipping "+(finish-begin)+"</font>\n");
        for(j=0;j<half;++j) {
          temp = solution[begin+j];
          solution[begin+j]=solution[finish-1-j];
          solution[finish-1-j]=temp;
        }
        UpdateProgress(len,1);
      }
    }
    return once;
  }
 
 
  protected float CalculateWeight(int a,int b) {
    float x = points[a].x - points[b].x;
    float y = points[a].y - points[b].y;
    return x*x+y*y;
  }
 
 
  private void GenerateTSP() {
    GreedyTour();

    Makelangelo.getSingleton().Log("<font color='green'>Running Lin/Kerighan optimization...</font>\n");

    len=GetTourLength(solution);
    old_len=len;
   
    t_elapsed=0;
    t_start = System.currentTimeMillis();
    progress=0;
    UpdateProgress(len,2);

    int once=1;
    while(once==1 && t_elapsed<time_limit && !parent.isCancelled()) {
      once=0;
      //@TODO: make these optional for the very thorough people
      //once|=transposeForwardTest();
      //once|=transposeBackwardTest();
      once|=flipTests();
      UpdateProgress(len,2);
    }
   
    ConvertAndSaveToGCode();
  }
 
   
  private double CalculateLength(int a,int b) {
    return Math.sqrt(CalculateWeight(a,b));
  }
 
  /**
   * Get the length of a tour segment
   * @param list an array of indexes into the point list.  the order forms the tour sequence.
   * @param start the index of the first point of the tour segment
   * @param end the index of the last point of the tour segment
   * @return the length of the tour
   */
  private double GetTourLength(int[] list) {
    double w=0;
    for(int i=0;i<numPoints-1;++i) {
      w+=CalculateLength(list[i],list[i+1]);
    }
    return w;
  }

  /**
   * Starting with point 0, find the next nearest point and repeat until all points have been "found".
   */
  private void GreedyTour() {
    Makelangelo.getSingleton().Log("<font color='green'>Finding greedy tour solution...</font>\n");

    int i;
    float w, bestw;
    int besti;
   
    // put all the points in the solution in no particular order.
    for(i=0;i<numPoints;++i) {
      solution[i]=i;
    }
   

    int scount=0;
    solution[scount]=solution[0];
   
    do {
      // Find the nearest point not already in the line.
      // Any solution[n] where n>scount is not in the line.
      bestw=CalculateWeight(solution[scount],solution[scount+1]);
      besti=scount+1;
      for( i=scount+2; i<numPoints; ++i ) {
        w=CalculateWeight(solution[scount],solution[i]);
        if( w < bestw ) {
          bestw=w;
          besti=i;
        }
      }
      i=solution[scount+1];
      solution[scount+1]=solution[besti];
      solution[besti]=i;
      scount++;
    } while(scount<numPoints);
  }
 

  private void MoveTo(OutputStreamWriter out,int i,boolean up) throws IOException {
    tool.WriteMoveTo(out, points[solution[i]].x, points[solution[i]].y);
  }
 
 
  /**
   * Open a file and write out the edge list as a set of GCode commands.
   * Since all the points are connected in a single loop,
   * start at the tsp point closest to the calibration point and go around until you get back to the start.
   */
  private void ConvertAndSaveToGCode() {
    // find the tsp point closest to the calibration point
    int i;
    int besti=-1;
    float bestw=1000000;
    float x,y,w;
    for(i=0;i<numPoints;++i) {
      x=points[solution[i]].x;
      y=points[solution[i]].y;
      w=x*x+y*y;
      if(w<bestw) {
        bestw=w;
        besti=i;
      }
    }
   
    // write
    try {
      OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(dest),"UTF-8");
      out.write(MachineConfiguration.getSingleton().GetConfigLine()+";\n");
      out.write(MachineConfiguration.getSingleton().GetBobbinLine()+";\n");

      //MachineConfiguration mc = MachineConfiguration.getSingleton();
      //tool = mc.GetCurrentTool();
     
      SetAbsoluteMode(out);
      tool.WriteChangeTo(out);
      liftPen(out);
      // move to the first point
      MoveTo(out,besti,false);
      lowerPen(out);

      for(i=1;i<numPoints;++i) {
        MoveTo(out,(besti+i)%numPoints,false);
      }
      MoveTo(out,besti,false);
     
      // lift pen and return to home
      liftPen(out);
      SignName(out);
      tool.WriteMoveTo(out,0,0);
      out.close();
    }
    catch(IOException e) {
      Makelangelo.getSingleton().Log("<font color='red'>Error saving "+dest+": "+e.getMessage()+"</font>");
    }
  }
 
 
  protected void ConnectTheDots(BufferedImage img) {
    MachineConfiguration mc = MachineConfiguration.getSingleton();
    tool = mc.GetCurrentTool();
    ImageSetupTransform(img);

    int x,y,i;
    // count the points
    numPoints=0;
    for(y=0;y<image_height;++y) {
      for(x=0;x<image_width;++x) {
        i=decode(img.getRGB(x,y));
        if(i==0) {
          ++numPoints;
        }
      }
    }
   
    Makelangelo.getSingleton().Log("<font color='green'>"+numPoints + " points,</font>\n");
    points = new Point2D[numPoints+1];
    solution = new int[numPoints+1];
 
    // collect the point data
    numPoints=0;
    for(y=0;y<image_height;++y) {
      for(x=0;x<image_width;++x) {
        i=decode(img.getRGB(x,y));
        if(i==0) {
          points[numPoints++]=new Point2D(TX(x),TY(y));
        }
      }
    }
  }
 
  /**
   * The main entry point
   * @param img the image to convert.
   */
  public void Convert(BufferedImage img) {
    // resize & flip as needed
    Filter_Resize rs = new Filter_Resize(250,250);
    img = rs.Process(img);
    // make black & white
    Filter_BlackAndWhite bw = new Filter_BlackAndWhite(255);
    img = bw.Process(img);
    // dither
    Filter_DitherFloydSteinberg dither = new Filter_DitherFloydSteinberg();
    img = dither.Process(img);
    // connect the dots
    ConnectTheDots(img);
    // Shorten the line that connects the dots
    GenerateTSP();
  }
}


/**
* This file is part of DrawbotGUI.
*
* DrawbotGUI is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DrawbotGUI 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
*/
TOP

Related Classes of Filters.Filter_GeneratorTSP

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.