40 package eu.mihosoft.vrl.v3d.svg;
42 import java.awt.image.BufferedImage;
43 import java.io.BufferedWriter;
45 import java.io.FileWriter;
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.Map.Entry;
49 import java.util.TreeMap;
51 import javax.imageio.ImageIO;
58 for(
int j=0; j<arr.length; j++ ){
if(arr[j].toLowerCase().equals(str)){
return j; } }
return -1;
61 public static float parsenext(String [] arr,
int i){
62 if(i<(arr.length-1)){
try{
return Float.parseFloat(arr[i+1]); }
catch(Exception e){} }
return -1;
66 public static class IndexedImage{
67 public int width, height;
68 public int [][] array;
69 public byte [][] palette;
70 public ArrayList<ArrayList<ArrayList<Double[]>>> layers;
72 public IndexedImage(
int [][] marray,
byte [][] mpalette){
73 array = marray; palette = mpalette;
74 width = marray[0].length-2; height = marray.length-2;
79 public static class ImageData{
80 public int width, height;
82 public ImageData(
int mwidth,
int mheight,
byte[] mdata){
83 width = mwidth; height = mheight; data = mdata;
88 public static void saveString(String filename, String str)
throws Exception {
89 File file =
new File(filename);
91 if(!file.exists()){ file.createNewFile(); }
92 FileWriter fw =
new FileWriter(file.getAbsoluteFile());
93 BufferedWriter bw =
new BufferedWriter(fw);
99 public static ImageData
loadImageData(String filename)
throws Exception {
100 BufferedImage image = ImageIO.read(
new File(filename));
103 public static ImageData
loadImageData(BufferedImage image)
throws Exception {
104 int width = image.getWidth();
int height = image.getHeight();
105 int[] rawdata = image.getRGB(0, 0, width, height,
null, 0, width);
106 byte[] data =
new byte[rawdata.length*4];
107 for(
int i=0; i<rawdata.length; i++){
108 data[(i*4)+3] =
bytetrans((
byte)(rawdata[i] >>> 24));
109 data[i*4 ] =
bytetrans((
byte)(rawdata[i] >>> 16));
110 data[(i*4)+1] =
bytetrans((
byte)(rawdata[i] >>> 8));
111 data[(i*4)+2] =
bytetrans((
byte)(rawdata[i]));
113 return new ImageData(width,height,data);
120 if(b<0){
return (
byte)(b+128); }
else{
return (
byte)(b-128); }
130 public static String
imageToSVG (String filename, HashMap<String,Float> options,
byte [][] palette)
throws Exception{
135 public static String
imageToSVG(BufferedImage image, HashMap<String,Float> options,
byte [][] palette)
throws Exception{
142 public static String
imagedataToSVG (ImageData imgd, HashMap<String,Float> options,
byte [][] palette){
149 public IndexedImage
imageToTracedata(String filename, HashMap<String,Float> options,
byte [][] palette)
throws Exception{
154 public IndexedImage
imageToTracedata(BufferedImage image, HashMap<String,Float> options,
byte [][] palette)
throws Exception{
161 public static IndexedImage
imagedataToTracedata (ImageData imgd, HashMap<String,Float> options,
byte [][] palette){
167 ArrayList<ArrayList<ArrayList<Integer[]>>> bps =
batchpathscan(rawlayers,(
int)(Math.floor(options.get(
"pathomit"))));
169 ArrayList<ArrayList<ArrayList<Double[]>>> bis = batchinternodes(bps);
171 ii.layers =
batchtracelayers(bis,options.get(
"ltres"),options.get(
"qtres"));
176 public static HashMap<String,Float>
checkoptions(HashMap<String,Float> options){
177 if(options==
null){ options =
new HashMap<String,Float>(); }
179 if(!options.containsKey(
"ltres")){ options.put(
"ltres",1f); }
180 if(!options.containsKey(
"qtres")){ options.put(
"qtres",1f); }
181 if(!options.containsKey(
"pathomit")){ options.put(
"pathomit",8f); }
183 if(!options.containsKey(
"colorsampling")){ options.put(
"colorsampling",1f); }
184 if(!options.containsKey(
"numberofcolors")){ options.put(
"numberofcolors",16f); }
185 if(!options.containsKey(
"mincolorratio")){ options.put(
"mincolorratio",0.02f); }
186 if(!options.containsKey(
"colorquantcycles")){ options.put(
"colorquantcycles",3f); }
188 if(!options.containsKey(
"scale")){ options.put(
"scale",1f); }
189 if(!options.containsKey(
"simplifytolerance")){ options.put(
"simplifytolerance",0f); }
190 if(!options.containsKey(
"roundcoords")){ options.put(
"roundcoords",1f); }
191 if(!options.containsKey(
"lcpr")){ options.put(
"lcpr",0f); }
192 if(!options.containsKey(
"qcpr")){ options.put(
"qcpr",0f); }
193 if(!options.containsKey(
"desc")){ options.put(
"desc",1f); }
194 if(!options.containsKey(
"viewbox")){ options.put(
"viewbox",0f); }
196 if(!options.containsKey(
"blurradius")){ options.put(
"blurradius",0f); }
197 if(!options.containsKey(
"blurdelta")){ options.put(
"blurdelta",20f); }
211 public static IndexedImage
colorquantization (ImageData imgd,
byte [][] palette, HashMap<String,Float> options){
212 int numberofcolors = (int)Math.floor(options.get(
"numberofcolors"));
float minratio = options.get(
"mincolorratio");
int cycles = (int)Math.floor(options.get(
"colorquantcycles"));
214 int [][] arr =
new int[imgd.height+2][imgd.width+2];
215 for(
int j=0; j<(imgd.height+2); j++){ arr[j][0] = -1; arr[j][imgd.width+1 ] = -1; }
216 for(
int i=0; i<(imgd.width+2) ; i++){ arr[0][i] = -1; arr[imgd.height+1][i] = -1; }
218 int idx=0, cd,cdl,ci,c1,c2,c3,c4;
222 if(options.get(
"colorsampling")!=0){
230 if( options.get(
"blurradius") > 0 ){ imgd = blur( imgd, options.get(
"blurradius"), options.get(
"blurdelta") ); }
232 long [][] paletteacc =
new long[palette.length][5];
235 for(
int cnt=0;cnt<cycles;cnt++){
241 for(
int k=0;k<palette.length;k++){
243 if(paletteacc[k][3]>0){
244 palette[k][0] = (byte) (-128+Math.floor(paletteacc[k][0]/paletteacc[k][4]));
245 palette[k][1] = (byte) (-128+Math.floor(paletteacc[k][1]/paletteacc[k][4]));
246 palette[k][2] = (byte) (-128+Math.floor(paletteacc[k][2]/paletteacc[k][4]));
247 palette[k][3] = (byte) (-128+Math.floor(paletteacc[k][3]/paletteacc[k][4]));
249 ratio = paletteacc[k][4]/(imgd.width*imgd.height);
252 if((ratio<minratio)&&(cnt<(cycles-1))){
253 palette[k][0] = (byte) (-128+Math.floor(Math.random()*255));
254 palette[k][1] = (byte) (-128+Math.floor(Math.random()*255));
255 palette[k][2] = (byte) (-128+Math.floor(Math.random()*255));
256 palette[k][3] = (byte) (-128+Math.floor(Math.random()*255));
263 for(
int i=0;i<palette.length;i++){
272 for(
int j=0;j<imgd.height;j++){
273 for(
int i=0;i<imgd.width;i++){
275 idx = ((j*imgd.width)+i)*4;
278 cdl = 256+256+256+256; ci=0;
279 for(
int k=0;k<palette.length;k++){
282 c1 = Math.abs(palette[k][0]-imgd.data[idx]);
283 c2 = Math.abs(palette[k][1]-imgd.data[idx+1]);
284 c3 = Math.abs(palette[k][2]-imgd.data[idx+2]);
285 c4 = Math.abs(palette[k][3]-imgd.data[idx+3]);
286 cd = c1+c2+c3+(c4*4);
289 if(cd<cdl){ cdl = cd; ci = k; }
294 paletteacc[ci][0] += 128+imgd.data[idx];
295 paletteacc[ci][1] += 128+imgd.data[idx+1];
296 paletteacc[ci][2] += 128+imgd.data[idx+2];
297 paletteacc[ci][3] += 128+imgd.data[idx+3];
306 return new IndexedImage(arr, palette);
311 byte [][] palette =
new byte[numberofcolors][4];
312 if(numberofcolors<8){
315 byte graystep = (byte) Math.floor(255/(numberofcolors-1));
316 for(
byte ccnt=0;ccnt<numberofcolors;ccnt++){
317 palette[ccnt][0] = (byte)(-128+(ccnt*graystep));
318 palette[ccnt][1] = (byte)(-128+(ccnt*graystep));
319 palette[ccnt][2] = (byte)(-128+(ccnt*graystep));
320 palette[ccnt][3] = (byte)255;
326 int colorqnum = (int) Math.floor(Math.pow(numberofcolors, 1.0/3.0));
327 int colorstep = (int) Math.floor(255/(colorqnum-1));
329 for(
int rcnt=0;rcnt<colorqnum;rcnt++){
330 for(
int gcnt=0;gcnt<colorqnum;gcnt++){
331 for(
int bcnt=0;bcnt<colorqnum;bcnt++){
332 palette[ccnt][0] = (byte)(-128+(rcnt*colorstep));
333 palette[ccnt][1] = (byte)(-128+(gcnt*colorstep));
334 palette[ccnt][2] = (byte)(-128+(bcnt*colorstep));
335 palette[ccnt][3] = (byte)127;
342 for(
int rcnt=ccnt;rcnt<numberofcolors;rcnt++){
343 palette[ccnt][0] = (byte)(-128+Math.floor(Math.random()*255));
344 palette[ccnt][1] = (byte)(-128+Math.floor(Math.random()*255));
345 palette[ccnt][2] = (byte)(-128+Math.floor(Math.random()*255));
346 palette[ccnt][3] = (byte)(-128+Math.floor(Math.random()*255));
355 int idx=0;
byte [][] palette =
new byte[numberofcolors][4];
356 for(
int i=0; i<numberofcolors; i++){
357 idx = (int) (Math.floor( (Math.random() * imgd.data.length) / 4 ) * 4);
358 palette[i][0] = imgd.data[idx ];
359 palette[i][1] = imgd.data[idx+1];
360 palette[i][2] = imgd.data[idx+2];
361 palette[i][3] = imgd.data[idx+3];
372 public static int[][][]
layering (IndexedImage ii){
374 int val=0, aw = ii.array[0].length, ah = ii.array.length, n1,n2,n3,n4,n5,n6,n7,n8;
375 int[][][] layers =
new int[ii.palette.length][ah][aw];
378 for(
int j=1; j<(ah-1); j++){
379 for(
int i=1; i<(aw-1); i++){
382 val = ii.array[j][i];
385 if((j>0) && (i>0)) { n1 = ii.array[j-1][i-1]==val?1:0; }
else{ n1 = 0; }
386 if (j>0) { n2 = ii.array[j-1][i ]==val?1:0; }
else{ n2 = 0; }
387 if((j>0) && (i<(aw-1))){ n3 = ii.array[j-1][i+1]==val?1:0; }
else{ n3 = 0; }
388 if (i>0) { n4 = ii.array[j ][i-1]==val?1:0; }
else{ n4 = 0; }
389 if (i<(aw-1)) { n5 = ii.array[j ][i+1]==val?1:0; }
else{ n5 = 0; }
390 if((j<(ah-1)) && (i>0)) { n6 = ii.array[j+1][i-1]==val?1:0; }
else{ n6 = 0; }
391 if (j<(ah-1)) { n7 = ii.array[j+1][i ]==val?1:0; }
else{ n7 = 0; }
392 if((j<(ah-1)) && (i<(aw-1))){ n8 = ii.array[j+1][i+1]==val?1:0; }
else{ n8 = 0; }
395 layers[val][j+1][i+1] = 1 + (n5 * 2) + (n8 * 4) + (n7 * 8) ;
396 if(n4==0){ layers[val][j+1][i ] = 0 + 2 + (n7 * 4) + (n6 * 8) ; }
397 if(n2==0){ layers[val][j ][i+1] = 0 + (n3*2) + (n5 * 4) + 8 ; }
398 if(n1==0){ layers[val][j ][i ] = 0 + (n2*2) + 4 + (n4 * 8) ; }
413 public static ArrayList<ArrayList<Integer[]>>
pathscan (
int [][] arr,
float pathomit){
414 ArrayList<ArrayList<Integer[]>> paths =
new ArrayList<ArrayList<Integer[]>>();
415 ArrayList<Integer[]> thispath;
416 int px=0,py=0,w=arr[0].length,h=arr.length,dir=0;
417 boolean pathfinished=
true, holepath =
false;
419 for(
int j=0;j<h;j++){
420 for(
int i=0;i<w;i++){
421 if((arr[j][i]!=0)&&(arr[j][i]!=15)){
424 paths.add(
new ArrayList<Integer[]>());
425 thispath = paths.get(paths.size()-1);
426 pathfinished =
false;
428 if(arr[py][px]==1){dir = 0;}
429 if(arr[py][px]==2){dir = 3;}
430 if(arr[py][px]==3){dir = 0;}
431 if(arr[py][px]==4){dir = 1; holepath =
false;}
432 if(arr[py][px]==5){dir = 0;}
433 if(arr[py][px]==6){dir = 3;}
434 if(arr[py][px]==7){dir = 0; holepath =
true;}
435 if(arr[py][px]==8){dir = 0;}
436 if(arr[py][px]==9){dir = 3;}
437 if(arr[py][px]==10){dir = 3;}
438 if(arr[py][px]==11){dir = 1; holepath =
true;}
439 if(arr[py][px]==12){dir = 0;}
440 if(arr[py][px]==13){dir = 3; holepath =
true;}
441 if(arr[py][px]==14){dir = 0; holepath =
true;}
443 while(!pathfinished){
446 thispath.add(
new Integer[3]);
447 thispath.get(thispath.size()-1)[0] = px-1;
448 thispath.get(thispath.size()-1)[1] = py-1;
449 thispath.get(thispath.size()-1)[2] = arr[py][px];
458 }
else{pathfinished=
true;paths.remove(thispath);}
461 else if(arr[py][px]==2){
467 }
else{pathfinished=
true;paths.remove(thispath);}
470 else if(arr[py][px]==3){
476 }
else{pathfinished=
true;paths.remove(thispath);}
479 else if(arr[py][px]==4){
485 }
else{pathfinished=
true;paths.remove(thispath);}
488 else if(arr[py][px]==5){
490 arr[py][px] = 13;py++;dir=3;
492 arr[py][px] = 13;px--;dir=2;
494 arr[py][px] = 7;py--;dir=1;
496 arr[py][px] = 7;px++;dir=0;
500 else if(arr[py][px]==6){
506 }
else{pathfinished=
true;paths.remove(thispath);}
509 else if(arr[py][px]==7){
515 }
else{pathfinished=
true;paths.remove(thispath);}
518 else if(arr[py][px]==8){
524 }
else{pathfinished=
true;paths.remove(thispath);}
527 else if(arr[py][px]==9){
533 }
else{pathfinished=
true;paths.remove(thispath);}
536 else if(arr[py][px]==10){
538 arr[py][px] = 11;py--;dir=1;
540 arr[py][px] = 14;px++;dir=0;
542 arr[py][px] = 14;py++;dir=3;
544 arr[py][px] = 11;px--;dir=2;
548 else if(arr[py][px]==11){
554 }
else{pathfinished=
true;paths.remove(thispath);}
557 else if(arr[py][px]==12){
563 }
else{pathfinished=
true;paths.remove(thispath);}
566 else if(arr[py][px]==13){
572 }
else{pathfinished=
true;paths.remove(thispath);}
575 else if(arr[py][px]==14){
581 }
else{pathfinished=
true;paths.remove(thispath);}
585 if(((px-1)==thispath.get(0)[0])&&((py-1)==thispath.get(0)[1])){
588 if( (holepath) || (thispath.size()<pathomit) ){
589 paths.remove(thispath);
605 public static ArrayList<ArrayList<ArrayList<Integer[]>>>
batchpathscan (
int [][][] layers,
float pathomit){
606 ArrayList<ArrayList<ArrayList<Integer[]>>> bpaths =
new ArrayList<ArrayList<ArrayList<Integer[]>>>();
607 for (
int[][] layer : layers) {
608 bpaths.add(
pathscan(layer,pathomit));
614 public static ArrayList<ArrayList<Double[]>>
internodes (ArrayList<ArrayList<Integer[]>> paths){
615 ArrayList<ArrayList<Double[]>> ins =
new ArrayList<ArrayList<Double[]>>();
616 ArrayList<Double[]> thisinp;
617 Double[] thispoint, nextpoint =
new Double[2];
618 Integer[] pp1, pp2, pp3;
620 int palen=0,nextidx=0,nextidx2=0;
622 for(
int pacnt=0; pacnt<paths.size(); pacnt++){
623 ins.add(
new ArrayList<Double[]>());
624 thisinp = ins.get(ins.size()-1);
625 palen = paths.get(pacnt).size();
627 for(
int pcnt=0;pcnt<palen;pcnt++){
630 nextidx = (pcnt+1)%palen; nextidx2 = (pcnt+2)%palen;
631 thisinp.add(
new Double[3]);
632 thispoint = thisinp.get(thisinp.size()-1);
633 pp1 = paths.get(pacnt).get(pcnt);
634 pp2 = paths.get(pacnt).get(nextidx);
635 pp3 = paths.get(pacnt).get(nextidx2);
636 thispoint[0] = (pp1[0]+pp2[0]) / 2.0;
637 thispoint[1] = (pp1[1]+pp2[1]) / 2.0;
638 nextpoint[0] = (pp2[0]+pp3[0]) / 2.0;
639 nextpoint[1] = (pp2[1]+pp3[1]) / 2.0;
643 if(thispoint[0] < nextpoint[0]){
644 if (thispoint[1] < nextpoint[1]){ thispoint[2] = 1.0; }
645 else if(thispoint[1] > nextpoint[1]){ thispoint[2] = 7.0; }
646 else { thispoint[2] = 0.0; }
647 }
else if(thispoint[0] > nextpoint[0]){
648 if (thispoint[1] < nextpoint[1]){ thispoint[2] = 3.0; }
649 else if(thispoint[1] > nextpoint[1]){ thispoint[2] = 5.0; }
650 else { thispoint[2] = 4.0; }
652 if (thispoint[1] < nextpoint[1]){ thispoint[2] = 2.0; }
653 else if(thispoint[1] > nextpoint[1]){ thispoint[2] = 6.0; }
654 else { thispoint[2] = 8.0; }
665 static ArrayList<ArrayList<ArrayList<Double[]>>> batchinternodes (ArrayList<ArrayList<ArrayList<Integer[]>>> bpaths){
666 ArrayList<ArrayList<ArrayList<Double[]>>> binternodes =
new ArrayList<ArrayList<ArrayList<Double[]>>>();
667 for(
int k=0; k<bpaths.size(); k++) {
691 public static ArrayList<Double[]>
tracepath (ArrayList<Double[]> path,
float ltreshold,
float qtreshold){
692 int pcnt=0, seqend=0;
double segtype1, segtype2;
693 ArrayList<Double[]> smp =
new ArrayList<Double[]>();
695 int pathlength = path.size();
697 while(pcnt<pathlength){
699 segtype1 = path.get(pcnt)[2]; segtype2 = -1; seqend=pcnt+1;
701 ((path.get(seqend)[2]==segtype1) || (path.get(seqend)[2]==segtype2) || (segtype2==-1))
702 && (seqend<(pathlength-1))){
703 if((path.get(seqend)[2]!=segtype1) && (segtype2==-1)){ segtype2 = path.get(seqend)[2];}
706 if(seqend==(pathlength-1)){ seqend = 0; }
709 smp.addAll(
fitseq(path,ltreshold,qtreshold,pcnt,seqend));
713 if(seqend>0){ pcnt = seqend; }
else{ pcnt = pathlength; }
723 public static ArrayList<Double[]>
fitseq (ArrayList<Double[]> path,
float ltreshold,
float qtreshold,
int seqstart,
int seqend){
724 ArrayList<Double[]> segment =
new ArrayList<Double[]>();
725 Double [] thissegment;
726 int pathlength = path.size();
729 if((seqend>pathlength)||(seqend<0)){
return segment;}
731 int errorpoint=seqstart;
732 boolean curvepass=
true;
733 double px, py, dist2, errorval=0;
734 double tl = (seqend-seqstart);
if(tl<0){ tl += pathlength; }
735 double vx = (path.get(seqend)[0]-path.get(seqstart)[0]) / tl,
736 vy = (path.get(seqend)[1]-path.get(seqstart)[1]) / tl;
739 int pcnt = (seqstart+1)%pathlength;
741 while(pcnt != seqend){
742 pl = pcnt-seqstart;
if(pl<0){ pl += pathlength; }
743 px = path.get(seqstart)[0] + (vx * pl); py = path.get(seqstart)[1] + (vy * pl);
744 dist2 = ((path.get(pcnt)[0]-px)*(path.get(pcnt)[0]-px)) + ((path.get(pcnt)[1]-py)*(path.get(pcnt)[1]-py));
745 if(dist2>ltreshold){curvepass=
false;}
746 if(dist2>errorval){ errorpoint=pcnt; errorval=dist2; }
747 pcnt = (pcnt+1)%pathlength;
752 segment.add(
new Double[7]);
753 thissegment = segment.get(segment.size()-1);
754 thissegment[0] = 1.0;
755 thissegment[1] = path.get(seqstart)[0];
756 thissegment[2] = path.get(seqstart)[1];
757 thissegment[3] = path.get(seqend)[0];
758 thissegment[4] = path.get(seqend)[1];
759 thissegment[5] = 0.0;
760 thissegment[6] = 0.0;
765 int fitpoint = errorpoint; curvepass =
true; errorval = 0;
769 double t=(fitpoint-seqstart)/tl, t1=(1.0-t)*(1.0-t), t2=2.0*(1.0-t)*t, t3=t*t;
770 double cpx = (((t1*path.get(seqstart)[0]) + (t3*path.get(seqend)[0])) - path.get(fitpoint)[0])/-t2 ,
771 cpy = (((t1*path.get(seqstart)[1]) + (t3*path.get(seqend)[1])) - path.get(fitpoint)[1])/-t2 ;
775 while(pcnt != seqend){
777 t=(pcnt-seqstart)/tl; t1=(1.0-t)*(1.0-t); t2=2.0*(1.0-t)*t; t3=t*t;
778 px = (t1 * path.get(seqstart)[0]) + (t2 * cpx) + (t3 * path.get(seqend)[0]);
779 py = (t1 * path.get(seqstart)[1]) + (t2 * cpy) + (t3 * path.get(seqend)[1]);
781 dist2 = ((path.get(pcnt)[0]-px)*(path.get(pcnt)[0]-px)) + ((path.get(pcnt)[1]-py)*(path.get(pcnt)[1]-py));
783 if(dist2>qtreshold){curvepass=
false;}
784 if(dist2>errorval){ errorpoint=pcnt; errorval=dist2; }
785 pcnt = (pcnt+1)%pathlength;
790 segment.add(
new Double[7]);
791 thissegment = segment.get(segment.size()-1);
792 thissegment[0] = 2.0;
793 thissegment[1] = path.get(seqstart)[0];
794 thissegment[2] = path.get(seqstart)[1];
795 thissegment[3] = cpx;
796 thissegment[4] = cpy;
797 thissegment[5] = path.get(seqend)[0];
798 thissegment[6] = path.get(seqend)[1];
804 int splitpoint = (fitpoint + errorpoint)/2;
807 segment =
fitseq(path,ltreshold,qtreshold,seqstart,splitpoint);
808 segment.addAll(
fitseq(path,ltreshold,qtreshold,splitpoint,seqend));
814 public static ArrayList<ArrayList<Double[]>>
batchtracepaths(ArrayList<ArrayList<Double[]>> internodepaths,
float ltres,
float qtres){
815 ArrayList<ArrayList<Double[]>> btracedpaths =
new ArrayList<ArrayList<Double[]>>();
816 for(
int k=0;k<internodepaths.size();k++){
817 btracedpaths.add(
tracepath(internodepaths.get(k),ltres,qtres) );
823 public static ArrayList<ArrayList<ArrayList<Double[]>>>
batchtracelayers(ArrayList<ArrayList<ArrayList<Double[]>>> binternodes,
float ltres,
float qtres){
824 ArrayList<ArrayList<ArrayList<Double[]>>> btbis =
new ArrayList<ArrayList<ArrayList<Double[]>>>();
825 for(
int k=0; k<binternodes.size(); k++){
838 return (
float)(Math.round(val*Math.pow(10,places))/Math.pow(10,places));
842 public static void svgpathstring(StringBuilder sb, String desc, ArrayList<Double[]> segments, String colorstr, HashMap<String,Float> options){
843 float scale = options.get(
"scale");
844 float lcpr = options.get(
"lcpr");
845 float qcpr = options.get(
"qcpr");
846 float roundcoords = (float) Math.floor(options.get(
"roundcoords"));
854 .append(segments.get(0)[1]*scale)
856 .append(segments.get(0)[2]*scale)
859 if( roundcoords == -1 ){
860 for(
int pcnt=0;pcnt<segments.size();pcnt++){
861 if(segments.get(pcnt)[0]==1.0){
864 .append(segments.get(pcnt)[3]*scale)
866 .append(segments.get(pcnt)[4]*scale)
871 .append(segments.get(pcnt)[3]*scale)
873 .append(segments.get(pcnt)[4]*scale)
875 .append(segments.get(pcnt)[5]*scale)
877 .append(segments.get(pcnt)[6]*scale)
882 for(
int pcnt=0;pcnt<segments.size();pcnt++){
883 if(segments.get(pcnt)[0]==1.0){
886 .append(
roundtodec((
float)(segments.get(pcnt)[3]*scale),roundcoords))
888 .append(
roundtodec((
float)(segments.get(pcnt)[4]*scale),roundcoords))
893 .append(
roundtodec((
float)(segments.get(pcnt)[3]*scale),roundcoords))
895 .append(
roundtodec((
float)(segments.get(pcnt)[4]*scale),roundcoords)).append(
" ")
896 .append(
roundtodec((
float)(segments.get(pcnt)[5]*scale),roundcoords)).append(
" ")
897 .append(
roundtodec((
float)(segments.get(pcnt)[6]*scale),roundcoords)).append(
" ");
905 for(
int pcnt=0;pcnt<segments.size();pcnt++){
906 if((lcpr>0)&&(segments.get(pcnt)[0]==1.0)){
908 .append(
"<circle cx=\"")
909 .append(segments.get(pcnt)[3]*scale)
911 .append(segments.get(pcnt)[4]*scale)
914 .append(
"\" fill=\"white\" stroke-width=\"")
916 .append(
"\" stroke=\"black\" />");
918 if((qcpr>0)&&(segments.get(pcnt)[0]==2.0)){
920 .append(
"<circle cx=\"")
921 .append(segments.get(pcnt)[3]*scale)
923 .append(segments.get(pcnt)[4]*scale)
926 .append(
"\" fill=\"cyan\" stroke-width=\"")
928 .append(
"\" stroke=\"black\" />");
930 .append(
"<circle cx=\"")
931 .append(segments.get(pcnt)[5]*scale)
933 .append(segments.get(pcnt)[6]*scale)
936 .append(
"\" fill=\"white\" stroke-width=\"")
938 .append(
"\" stroke=\"black\" />");
940 .append(
"<line x1=\"")
941 .append(segments.get(pcnt)[1]*scale)
943 .append(segments.get(pcnt)[2]*scale)
945 .append(segments.get(pcnt)[3]*scale)
947 .append(segments.get(pcnt)[4]*scale)
948 .append(
"\" stroke-width=\"")
950 .append(
"\" stroke=\"cyan\" />");
952 .append(
"<line x1=\"")
953 .append(segments.get(pcnt)[3]*scale)
955 .append(segments.get(pcnt)[4]*scale)
957 .append(segments.get(pcnt)[5]*scale)
959 .append(segments.get(pcnt)[6]*scale)
960 .append(
"\" stroke-width=\"")
962 .append(
"\" stroke=\"cyan\" />");
971 public static String
getsvgstring(IndexedImage ii, HashMap<String,Float> options){
974 int w = (int) (ii.width * options.get(
"scale")), h = (
int) (ii.height * options.get(
"scale"));
975 String viewboxorviewport = options.get(
"viewbox")!=0 ?
"viewBox=\"0 0 "+w+
" "+h+
"\" " :
"width=\""+w+
"\" height=\""+h+
"\" ";
976 StringBuilder svgstr =
new StringBuilder(
"<svg "+viewboxorviewport+
"version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" ");
977 if(options.get(
"desc")!=0){ svgstr.append(
"desc=\"Created with ImageTracer.java version "+
ImageTracer.
versionnumber+
"\" "); }
979 svgstr.append(
"<g id=\"g37\">\n");
981 TreeMap <Double,Integer[]> zindex =
new TreeMap <Double,Integer[]>();
984 for(
int k=0; k<ii.layers.size(); k++) {
987 for(
int pcnt=0; pcnt<ii.layers.get(k).size(); pcnt++){
990 label = (ii.layers.get(k).get(pcnt).get(0)[2] * w) + ii.layers.get(k).get(pcnt).get(0)[1];
992 if(!zindex.containsKey(label)){ zindex.put(label,
new Integer[2]); }
994 zindex.get(label)[0] =
new Integer(k);
995 zindex.get(label)[1] =
new Integer(pcnt);
1004 String thisdesc =
"";
1005 for(Entry<Double, Integer[]> entry : zindex.entrySet()) {
1006 if(options.get(
"desc")!=0){ thisdesc =
"desc=\"l "+entry.getValue()[0]+
" p "+entry.getValue()[1]+
"\" "; }
else{ thisdesc =
""; }
1009 ii.layers.get(entry.getValue()[0]).get(entry.getValue()[1]),
1010 tosvgcolorstr(ii.palette[entry.getValue()[0]]),
1013 svgstr.append(
"</g>\n");
1015 svgstr.append(
"</svg>");
1017 return svgstr.toString();
1021 static String tosvgcolorstr(
byte[] c){
1025 return "fill=\"none\" stroke=\"rgb("+(0)+
","+(0)+
","+(0)+
")\" stroke-width=\"0.354\" opacity=\""+1+
"\" ";
1029 static double[][] gks = { {0.27901,0.44198,0.27901}, {0.135336,0.228569,0.272192,0.228569,0.135336}, {0.086776,0.136394,0.178908,0.195843,0.178908,0.136394,0.086776},
1030 {0.063327,0.093095,0.122589,0.144599,0.152781,0.144599,0.122589,0.093095,0.063327}, {0.049692,0.069304,0.089767,0.107988,0.120651,0.125194,0.120651,0.107988,0.089767,0.069304,0.049692} };
1033 static ImageData blur(ImageData imgd,
float rad,
float del){
1035 double racc,gacc,bacc,aacc,wacc;
1036 ImageData imgd2 =
new ImageData(imgd.width,imgd.height,
new byte[imgd.width*imgd.height*4]);
1039 int radius = (int)Math.floor(rad);
if(radius<1){
return imgd; }
if(radius>5){ radius = 5; }
1040 int delta = (int)Math.abs(del);
if(delta>1024){ delta = 1024; }
1041 double[] thisgk = gks[radius-1];
1044 for( j=0; j < imgd.height; j++ ){
1045 for( i=0; i < imgd.width; i++ ){
1047 racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0;
1049 for( k = -radius; k < (radius+1); k++){
1051 if( ((i+k) > 0) && ((i+k) < imgd.width) ){
1052 idx = ((j*imgd.width)+i+k)*4;
1053 racc += imgd.data[idx ] * thisgk[k+radius];
1054 gacc += imgd.data[idx+1] * thisgk[k+radius];
1055 bacc += imgd.data[idx+2] * thisgk[k+radius];
1056 aacc += imgd.data[idx+3] * thisgk[k+radius];
1057 wacc += thisgk[k+radius];
1061 idx = ((j*imgd.width)+i)*4;
1062 imgd2.data[idx ] = (byte) Math.floor(racc / wacc);
1063 imgd2.data[idx+1] = (byte) Math.floor(gacc / wacc);
1064 imgd2.data[idx+2] = (byte) Math.floor(bacc / wacc);
1065 imgd2.data[idx+3] = (byte) Math.floor(aacc / wacc);
1071 byte[] himgd = imgd2.data.clone();
1074 for( j=0; j < imgd.height; j++ ){
1075 for( i=0; i < imgd.width; i++ ){
1077 racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0;
1079 for( k = -radius; k < (radius+1); k++){
1081 if( ((j+k) > 0) && ((j+k) < imgd.height) ){
1082 idx = (((j+k)*imgd.width)+i)*4;
1083 racc += himgd[idx ] * thisgk[k+radius];
1084 gacc += himgd[idx+1] * thisgk[k+radius];
1085 bacc += himgd[idx+2] * thisgk[k+radius];
1086 aacc += himgd[idx+3] * thisgk[k+radius];
1087 wacc += thisgk[k+radius];
1091 idx = ((j*imgd.width)+i)*4;
1092 imgd2.data[idx ] = (byte) Math.floor(racc / wacc);
1093 imgd2.data[idx+1] = (byte) Math.floor(gacc / wacc);
1094 imgd2.data[idx+2] = (byte) Math.floor(bacc / wacc);
1095 imgd2.data[idx+3] = (byte) Math.floor(aacc / wacc);
1101 for( j=0; j < imgd.height; j++ ){
1102 for( i=0; i < imgd.width; i++ ){
1104 idx = ((j*imgd.width)+i)*4;
1106 d = Math.abs(imgd2.data[idx ] - imgd.data[idx ]) + Math.abs(imgd2.data[idx+1] - imgd.data[idx+1]) +
1107 Math.abs(imgd2.data[idx+2] - imgd.data[idx+2]) + Math.abs(imgd2.data[idx+3] - imgd.data[idx+3]);
1110 imgd2.data[idx ] = imgd.data[idx ];
1111 imgd2.data[idx+1] = imgd.data[idx+1];
1112 imgd2.data[idx+2] = imgd.data[idx+2];
1113 imgd2.data[idx+3] = imgd.data[idx+3];
static float roundtodec(float val, float places)
static ArrayList< Double[]> fitseq(ArrayList< Double[]> path, float ltreshold, float qtreshold, int seqstart, int seqend)
static String getsvgstring(IndexedImage ii, HashMap< String, Float > options)
static String imageToSVG(BufferedImage image, HashMap< String, Float > options, byte[][] palette)
static byte[][] generatepalette(int numberofcolors)
static ArrayList< ArrayList< Integer[]> > pathscan(int[][] arr, float pathomit)
static ImageData loadImageData(BufferedImage image)
IndexedImage imageToTracedata(BufferedImage image, HashMap< String, Float > options, byte[][] palette)
static float parsenext(String[] arr, int i)
static String imageToSVG(String filename, HashMap< String, Float > options, byte[][] palette)
static ArrayList< ArrayList< ArrayList< Double[]> > > batchtracelayers(ArrayList< ArrayList< ArrayList< Double[]>>> binternodes, float ltres, float qtres)
IndexedImage imageToTracedata(String filename, HashMap< String, Float > options, byte[][] palette)
static ArrayList< ArrayList< Double[]> > batchtracepaths(ArrayList< ArrayList< Double[]>> internodepaths, float ltres, float qtres)
static HashMap< String, Float > checkoptions(HashMap< String, Float > options)
static int[][][] layering(IndexedImage ii)
static byte[][] samplepalette(int numberofcolors, ImageData imgd)
static IndexedImage imagedataToTracedata(ImageData imgd, HashMap< String, Float > options, byte[][] palette)
static byte bytetrans(byte b)
static ImageData loadImageData(String filename)
static void svgpathstring(StringBuilder sb, String desc, ArrayList< Double[]> segments, String colorstr, HashMap< String, Float > options)
static int arraycontains(String[] arr, String str)
static String imagedataToSVG(ImageData imgd, HashMap< String, Float > options, byte[][] palette)
static void saveString(String filename, String str)
static ArrayList< ArrayList< ArrayList< Integer[]> > > batchpathscan(int[][][] layers, float pathomit)
static ArrayList< Double[]> tracepath(ArrayList< Double[]> path, float ltreshold, float qtreshold)
static String versionnumber
static IndexedImage colorquantization(ImageData imgd, byte[][] palette, HashMap< String, Float > options)
static ArrayList< ArrayList< Double[]> > internodes(ArrayList< ArrayList< Integer[]>> paths)