diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..221cb315 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/testrun.yml b/.github/workflows/testrun.yml index 8adf1062..d40c41e6 100644 --- a/.github/workflows/testrun.yml +++ b/.github/workflows/testrun.yml @@ -20,13 +20,3 @@ jobs: run: | echo Running install make install - - name: "Run a simple graph" - run: | - cd scripts - echo Trying to create graph - ./creategraphfromdate.sh 2000 09 - - name: Upload graph image - uses: actions/upload-artifact@v1 - with: - name: Upload graph - path: testrun/bview_2000_09/bview_2000_09_transparent.png \ No newline at end of file diff --git a/.gitignore b/.gitignore index dee24d5d..6d917b17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ tags +tagsfile *.o +.*.un~ +.*.swp +.swp /bin/lglayout2D /bin/lglayout3D /bin/lglbreakup @@ -24,4 +28,5 @@ src/lglbreakup src/lglfileconvert src/lglayout src/lglrebuild -**/*.pyx \ No newline at end of file +**/*.pyx +.DS_Store diff --git a/Java/Makefile b/Java/Makefile index fad9a43c..0ef97a28 100644 --- a/Java/Makefile +++ b/Java/Makefile @@ -10,7 +10,7 @@ jar/%.jar: $(CLASS) jarfiles: jar/ImageMaker.jar jar/LGLLib.jar jar/LGLView.jar %.class : %.java - javac -cp lib/Jama-1.0.3.jar:lib/VectorGraphics2D-0.13.jar src/Viewer2D/*.java src/ImageMaker/*.java + javac -cp lib/Jama-1.0.3.jar:lib/VectorGraphics2D-0.13.jar -cp src src/Viewer2D/*.java src/ImageMaker/*.java all: $(CLASS) diff --git a/Java/jar/ImageMaker.jar b/Java/jar/ImageMaker.jar index 14efc7d8..e9fce429 100644 Binary files a/Java/jar/ImageMaker.jar and b/Java/jar/ImageMaker.jar differ diff --git a/Java/jar/LGLLib.jar b/Java/jar/LGLLib.jar index a59394bd..2951c849 100644 Binary files a/Java/jar/LGLLib.jar and b/Java/jar/LGLLib.jar differ diff --git a/Java/jar/LGLView.jar b/Java/jar/LGLView.jar index 77195ec0..c2d4c9bd 100644 Binary files a/Java/jar/LGLView.jar and b/Java/jar/LGLView.jar differ diff --git a/Java/src/ImageMaker/GenerateImages.java b/Java/src/ImageMaker/GenerateImages.java index 51315f57..56fc1eff 100755 --- a/Java/src/ImageMaker/GenerateImages.java +++ b/Java/src/ImageMaker/GenerateImages.java @@ -1,321 +1,335 @@ -// -// Copyright (c) 2003 Alex Adai, All Rights Reserved. -// -// This program 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 2 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, -// MA 02111-1307 USA -// - -package ImageMaker; - -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; - -import Viewer2D.EdgesPanel; -import Viewer2D.FormatVertex; -import Viewer2D.ViewerIO; -import de.erichseifert.vectorgraphics2d.Document; -import de.erichseifert.vectorgraphics2d.Processor; -import de.erichseifert.vectorgraphics2d.Processors; -import de.erichseifert.vectorgraphics2d.VectorGraphics2D; -import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; -import de.erichseifert.vectorgraphics2d.util.PageSize; - -/** - * SESS - 2014.05.10: - * - * - */ -public class GenerateImages { - public static Color EDGE_COLOR = Color.white; - public static String EDGE_COLOR_FILE = "color_file"; - - public static void main(String[] args) { - boolean loadedEdgeColors = false; - if (args.length == 0) { - message(); - } - - int[] windowSizes = new int[2]; - windowSizes[0] = new Integer(args[0]).intValue(); - windowSizes[1] = new Integer(args[1]).intValue(); - System.out.println("Loading flindeberg mod 2"); - System.out.println("Image size is " + windowSizes[0] + " x " - + windowSizes[1]); - - // Check params - String edgeFile = args[2]; - String edgeColorFile = EDGE_COLOR_FILE; - List coordFiles = new ArrayList(); - boolean colorFileSwitch = false; - for (int i = 3; i < args.length; i++) { - String arg = args[i]; - if ("-c".equals(arg)) { - colorFileSwitch = true; - continue; - } - if (colorFileSwitch) { - edgeColorFile = arg; - break; - } - coordFiles.add(arg); - } - if (coordFiles.isEmpty()) { - message(); - } - - System.out.println("Loading edge file: " + edgeFile + "..."); - ViewerIO verterIO = null; - try { - verterIO = new ViewerIO(new File(edgeFile)); - verterIO.loadSHORTFile(); - } catch (IOException e) { - System.out.println(e.getMessage()); - System.exit(1); - } - System.out.println("Trying to load edge color file: " + edgeColorFile - + "..."); - try { - verterIO.loadEdgeColorFile(new File(edgeColorFile)); - loadedEdgeColors = true; - } catch (java.io.FileNotFoundException e) { - loadedEdgeColors = false; - System.out.println(e.getMessage()); - } catch (java.io.IOException e) { - System.out.println(e.getMessage()); - System.exit(1); - } - System.out.println("Edges loading complete."); - - generateDark(loadedEdgeColors, windowSizes, coordFiles, verterIO); - - generateLight(loadedEdgeColors, windowSizes, coordFiles, verterIO); - - generateTransparent(loadedEdgeColors, windowSizes, coordFiles, verterIO); - - // System.out.println("Going for vector graphics (currently broken)"); - // // Lets process coords files - // for (String coordFile : coordFiles) { - // try { - // System.out.println("Loading " + coordFile + "..."); - // verterIO.loadVertexCoords(new File(coordFile)); - - - // FormatVertex formatter = new FormatVertex( - // verterIO.getVertices(), verterIO.getStats(), - // windowSizes, 1); - - // EdgesPanel panel = new EdgesPanel(verterIO.getEdges(), - // verterIO.getVertices(), windowSizes[0], windowSizes[1]); - - // if (loadedEdgeColors) - // panel.addEdgeColors(verterIO.getEdgeColorMap()); - - // panel.showVertices(true); - // panel.setVisibilityTest(true); - // panel.setFormatter(formatter); - // panel.setEdgeColor(EDGE_COLOR); - // panel.setVertexColor(Color.white); - // panel.setBackgroundColor(new Color(0f,0f,0f,0f)); - - // // Use vector instead - // VectorGraphics2D g2 = new VectorGraphics2D(); - - // // Now the image has to be fitted to the given region - // panel.fitData(); - // panel.writeVectorImage(g2); - - // CommandSequence commands = ((VectorGraphics2D) g2).getCommands(); - - // String[] elements = {"pdf", "eps", "svg"}; - // for (String ext : elements) { - - // Processor pdfProcessor = Processors.get(ext); - // Document doc = pdfProcessor.getDocument(commands, PageSize.A4); - - // String pngFile = MessageFormat.format( - // "{0}_{1,number,0}x{2,number,0}_transparent.{3}", coordFile, - // windowSizes[0], windowSizes[1], ext); - - // System.out.println("Preparing " + pngFile + "..."); - // try { - // doc.writeTo(new FileOutputStream(pngFile)); - // } catch (Exception e) { - // System.out.println("Could not write vector graphics"); - // } - - // System.out.println("Done."); - // } - - - // } catch (IOException e) { - // System.out.println(MessageFormat.format( - // "Error processing {0}:\n{1}", e.getMessage())); - // } - // } - } - - private static void generateTransparent(boolean loadedEdgeColors, int[] windowSizes, List coordFiles, - ViewerIO verterIO) { - System.out.println("Going for transparent"); - // Lets process coords files - for (String coordFile : coordFiles) { - try { - System.out.println("Loading " + coordFile + "..."); - verterIO.loadVertexCoords(new File(coordFile)); - - String pngFile = MessageFormat.format( - "{0}_{1,number,0}x{2,number,0}_transparent.png", coordFile, - windowSizes[0], windowSizes[1]); - System.out.println("Preparing " + pngFile + "..."); - FormatVertex formatter = new FormatVertex( - verterIO.getVertices(), verterIO.getStats(), - windowSizes, 1); - - EdgesPanel panel = new EdgesPanel(verterIO.getEdges(), - verterIO.getVertices(), windowSizes[0], windowSizes[1]); - - if (loadedEdgeColors) - panel.addEdgeColors(verterIO.getEdgeColorMap()); - - panel.showVertices(true); - panel.setVisibilityTest(true); - panel.setFormatter(formatter); - panel.setEdgeColor(EDGE_COLOR); - panel.setVertexColor(Color.white); - panel.setBackgroundColor(new Color(0f,0f,0f,0f)); - - BufferedImage bufferedImage = new BufferedImage(windowSizes[0], - windowSizes[1], BufferedImage.TYPE_INT_ARGB); - - // Now the image has to be fitted to the given region - panel.fitData(); - panel.writeImage(pngFile, bufferedImage); - System.out.println("Done."); - } catch (IOException e) { - System.out.println(MessageFormat.format( - "Error processing {0}:\n{1}", e.getMessage())); - } - } - } - - private static void generateLight(boolean loadedEdgeColors, int[] windowSizes, List coordFiles, - ViewerIO verterIO) { - System.out.println("Going for light"); - // Lets process coords files - for (String coordFile : coordFiles) { - try { - System.out.println("Loading " + coordFile + "..."); - verterIO.loadVertexCoords(new File(coordFile)); - - String pngFile = MessageFormat.format( - "{0}_{1,number,0}x{2,number,0}_light.png", coordFile, - windowSizes[0], windowSizes[1]); - System.out.println("Preparing " + pngFile + "..."); - FormatVertex formatter = new FormatVertex( - verterIO.getVertices(), verterIO.getStats(), - windowSizes, 1); - - EdgesPanel panel = new EdgesPanel(verterIO.getEdges(), - verterIO.getVertices(), windowSizes[0], windowSizes[1]); - - if (loadedEdgeColors) - panel.addEdgeColors(verterIO.getEdgeColorMap()); - - panel.showVertices(true); - panel.setVisibilityTest(true); - panel.setFormatter(formatter); - panel.setEdgeColor(EDGE_COLOR); - panel.setVertexColor(Color.white); - panel.setBackgroundColor(Color.white); - - BufferedImage bufferedImage = new BufferedImage(windowSizes[0], - windowSizes[1], BufferedImage.TYPE_INT_ARGB); - - // Now the image has to be fitted to the given region - panel.fitData(); - panel.writeImage(pngFile, bufferedImage); - System.out.println("Done."); - } catch (IOException e) { - System.out.println(MessageFormat.format( - "Error processing {0}:\n{1}", e.getMessage())); - } - } - } - - private static void generateDark(boolean loadedEdgeColors, int[] windowSizes, List coordFiles, - ViewerIO verterIO) { - System.out.println("Going for dark"); - // Lets process coords files - for (String coordFile : coordFiles) { - try { - System.out.println("Loading " + coordFile + "..."); - verterIO.loadVertexCoords(new File(coordFile)); - - String pngFile = MessageFormat.format( - "{0}_{1,number,0}x{2,number,0}_dark.png", coordFile, - windowSizes[0], windowSizes[1]); - System.out.println("Preparing " + pngFile + "..."); - FormatVertex formatter = new FormatVertex( - verterIO.getVertices(), verterIO.getStats(), - windowSizes, 1); - - EdgesPanel panel = new EdgesPanel(verterIO.getEdges(), - verterIO.getVertices(), windowSizes[0], windowSizes[1]); - - if (loadedEdgeColors) - panel.addEdgeColors(verterIO.getEdgeColorMap()); - - panel.showVertices(true); - panel.setVisibilityTest(true); - panel.setFormatter(formatter); - panel.setEdgeColor(EDGE_COLOR); - panel.setVertexColor(Color.white); - panel.setBackgroundColor(Color.BLACK); - - BufferedImage bufferedImage = new BufferedImage(windowSizes[0], - windowSizes[1], BufferedImage.TYPE_INT_ARGB); - - // Now the image has to be fitted to the given region - panel.fitData(); - panel.writeImage(pngFile, bufferedImage); - System.out.println("Done."); - } catch (IOException e) { - System.out.println(MessageFormat.format( - "Error processing {0}:\n{1}", e.getMessage())); - } - } - } - - public static void message() { - System.out - .println("Arguments:\n\n" - + "\t ... [-c ]\n\n" - + "If no colors file specified program will try to load file named \"" - + EDGE_COLOR_FILE + "\".\n" - + "By default edges are white. flindeberg mod"); - System.exit(1); - } - -} +package ImageMaker; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import Viewer2D.EdgesPanel; +import Viewer2D.FormatVertex; +import Viewer2D.ViewerIO; + +public class GenerateImages { + + public static boolean GENERATE_IN_SEPARATE_THREAD = true; + + /** + * Entry point. + * + * @param args + */ + public static void main(String[] args) { + ParseArguments pa = new ParseArguments(false); + pa.parse(args); + + printStats(pa); + + GENERATE_IN_SEPARATE_THREAD = true; + + generate(pa, "dark with labels", "dark_withlabels", Color.BLACK, + true /*scale*/, true /*printLabels*/, true /*useAlignmentCenterArg*/); + generate(pa, "dark without labels", "dark_nolabels", Color.BLACK, + true /*scale*/, false /*printLabels*/, true /*useAlignmentCenterArg*/); + + generate(pa, "dark without scale without labels", "dark_withoutscale_withoutlabels", Color.BLACK, + false /*scale*/, false /*printLabels*/, false /*useAlignmentCenterArg*/); + generate(pa, "dark without scale with labels", "dark_withoutscale_withlabels", Color.BLACK, + false /*scale*/, true /*printLabels*/, false /*useAlignmentCenterArg*/); + + generate(pa, "light without scale without labels", "light_withoutscale_withoutlabels", Color.WHITE, + false /*scale*/, false /*printLabels*/, false /*useAlignmentCenterArg*/); + generate(pa, "transparent without scale without labels", "transparent_withoutscale_withoutlabels", new Color(0f, 0f, 0f, 0f), + false /*scale*/, false /*printLabels*/, false /*useAlignmentCenterArg*/); + } + + private static void printStats(ParseArguments pa) { + try { + ViewerIO v = createViewerIO(pa); + v.loadVertexCoords(new File(pa.coordFiles.get(0))); + v.getStats().print(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static ViewerIO createViewerIO(ParseArguments pa) { + ViewerIO verterIO = null; + try { + verterIO = new ViewerIO(new File(pa.edgeFile)); + verterIO.loadSHORTFile(); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + verterIO.setLabelScale(pa.scaling); + + verterIO.setMinMaxXY(pa.minX, pa.minY, pa.maxX, pa.maxY); + + try { + verterIO.loadEdgeColorFile(new File(pa.edgeColorFile)); + } catch (java.io.FileNotFoundException e) { + System.out.println(e.getMessage()); + } catch (java.io.IOException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + + return verterIO; + } + + private static void generate(ParseArguments pa, String displayname, String name, Color background, + boolean scale, boolean printLabels, boolean useAlignmentCenterArg) { + + ViewerIO verterIO = createViewerIO(pa); + + Runnable task = new Runnable() { + @Override + public void run() { + generate(displayname, name, background, !verterIO.getEdgeColorMap().isEmpty(), pa.windowSizes, pa.coordFiles, + printLabels ? pa.labelFile : "", useAlignmentCenterArg ? pa.alignmentCenter : false, verterIO, scale); + } + }; + + if (GENERATE_IN_SEPARATE_THREAD) { + new Thread(task).start(); + } + else { + task.run(); + } + } + + private static void generate(String displayname, String name, Color background, boolean loadedEdgeColors, + int[] windowSizes, List coordFiles, String labelFile, boolean alignmentCenter, ViewerIO verterIO, boolean scale) { + if (!labelFile.isEmpty()) + loadLabels(labelFile, verterIO); + else + verterIO.clearLabels(); + + for (String coordFile : coordFiles) { + try { + verterIO.loadVertexCoords(new File(coordFile)); + + FormatVertex formatter = new FormatVertex(verterIO.getVertices(), verterIO.getLabels(), + verterIO.getLabelScale(), scale ? verterIO.getMinX() : 0, scale ? verterIO.getMinY() : 0, + scale ? verterIO.getMaxX() : 0, scale ? verterIO.getMaxY() : 0, alignmentCenter, + verterIO.getStats(), windowSizes, 1); + + EdgesPanel panel = new EdgesPanel(verterIO.getEdges(), verterIO.getVertices(), verterIO.getLabels(), + windowSizes[0], windowSizes[1]); + + if (loadedEdgeColors) + panel.addEdgeColors(verterIO.getEdgeColorMap()); + + panel.showVertices(true); + panel.setVisibilityTest(true); + panel.setFormatter(formatter); + panel.setEdgeColor(EDGE_COLOR); + panel.setVertexColor(Color.white); + panel.setBackgroundColor(background); + + BufferedImage bufferedImage = new BufferedImage(windowSizes[0], windowSizes[1], + BufferedImage.TYPE_INT_ARGB); + + // Now the image has to be fitted to the given region + panel.fitData(); + String pngFile = MessageFormat.format("{0}_{1,number,0}x{2,number,0}_" + name + ".png", coordFile, + windowSizes[0], windowSizes[1]); + panel.writeImage(pngFile, bufferedImage); + System.out.println("Done."); + } catch (IOException e) { + System.out.println(MessageFormat.format("Error processing {0}:\n{1}", e.getMessage())); + } + } + } + + static void loadLabels(String labelFile, ViewerIO verterIO) { + if (labelFile.isEmpty()) return; + System.out.println("Loading label file: " + labelFile + "..."); + try { + verterIO.loadLabelFile(new File(labelFile)); + } catch (FileNotFoundException e) { + System.out.println(e.getMessage()); + } catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + System.out.println("Labels loading complete."); + } + + public static class ParseArguments { + + public String edgeFile; + public String edgeColorFile; + public String labelFile; + public double scaling; + public double minX; + public double maxX; + public double minY; + public double maxY; + int[] windowSizes; + boolean alignmentCenter; + public List coordFiles; + private boolean viewer2d; + + public ParseArguments(boolean viewer2d) { + this.viewer2d = viewer2d; + + } + + void usage() { + if (viewer2d) + message2(); + else + message(); + + } + + public void parse(String[] args) { + int minArguments = 3; + + if (viewer2d) { + minArguments = 1; + if (args.length == 0) + return; + } + + if (args.length < minArguments) { + usage(); + } + int argno = 0; + if (!viewer2d) { + windowSizes = new int[2]; + windowSizes[0] = Integer.parseInt(args[0]); + windowSizes[1] = Integer.parseInt(args[1]); + argno += 2; + System.out.println("Image size is " + windowSizes[0] + " x " + windowSizes[1]); + } + + // Check params + edgeFile = args[argno++]; + edgeColorFile = ""; + coordFiles = new ArrayList(); + labelFile = ""; + boolean colorFileSwitch = false; + boolean labelFileSwitch = false; + boolean scaleSwitch = false; + scaling = 1; + + boolean wasMin = false; + boolean wasMax = false; + boolean minSwitch = false; + boolean maxSwitch = false; + minX = 0; + maxX = 0; + minY = 0; + maxY = 0; + + alignmentCenter = false; + boolean alignSwitch = false; + for (int i = argno; i < args.length; i++) { + String arg = args[i]; + if ("-c".equals(arg)) { + colorFileSwitch = true; + continue; + } + if ("-l".equals(arg)) { + labelFileSwitch = true; + continue; + } + if ("-s".equals(arg)) { + scaleSwitch = true; + continue; + } + if ("-m".equals(arg) && !viewer2d) { + minSwitch = true; + continue; + } + if ("-a".equals(arg) && !viewer2d) { + alignSwitch = true; + continue; + } + if ("-M".equals(arg) && !viewer2d) { + maxSwitch = true; + continue; + } + if (scaleSwitch) { + scaleSwitch = false; + scaling = Double.parseDouble(arg); + continue; + } + if (minSwitch) { + minSwitch = false; + wasMin = true; + String[] a = arg.split(","); + if (a.length != 2) { + System.out.println("Error:-m requires exactly 2 coordinates"); + System.exit(1); + + } + minX = Double.parseDouble(a[0]); + minY = Double.parseDouble(a[1]); + continue; + } + if (maxSwitch) { + maxSwitch = false; + wasMax = true; + String[] a = arg.split(","); + if (a.length != 2) { + System.out.println("Error:-M requires exactly 2 coordinates"); + System.exit(1); + } + maxX = Double.parseDouble(a[0]); + maxY = Double.parseDouble(a[1]); + continue; + } + if (alignSwitch) { + alignSwitch = false; + if (arg.equals("center")) + alignmentCenter = true; + continue; + } + if (labelFileSwitch) { + labelFile = arg; + labelFileSwitch = false; + + continue; + } + if (colorFileSwitch) { + edgeColorFile = arg; + colorFileSwitch = false; + continue; + } + coordFiles.add(arg); + } + if (coordFiles.isEmpty()) { + usage(); + } + + if (wasMax ^ wasMin) { + System.out.println("Error:Both -m and -M need to be used at the same time, one of them is missing"); + System.exit(1); + } + } + } + + public static Color EDGE_COLOR = Color.white; + public static String EDGE_COLOR_FILE = "color_file"; + + public static void message() { + System.out.println("Arguments:\n\n" + + "\t ... [-c ] [-l ] [-m minx,miny -M -maxx,maxy] [-a center]\n\n" + + "If no colors file specified program will try to load file named \"" + EDGE_COLOR_FILE + "\".\n" + + "By default edges are white. flindeberg mod"); + System.exit(1); + } + + public static void message2() { + System.out.println("Arguments:\n\n" + "\t [-c ] [-l ]\n\n" + + "If no colors file specified program will try to load file named \"" + EDGE_COLOR_FILE + "\".\n"); + System.exit(1); + } +} \ No newline at end of file diff --git a/Java/src/Jama/ChangeLog b/Java/src/Jama/ChangeLog new file mode 100644 index 00000000..1557c282 --- /dev/null +++ b/Java/src/Jama/ChangeLog @@ -0,0 +1,42 @@ +2012-11-09 Bruce Miller + * Version 1.0.3 Released. + * Patched hqr2 method in Jama.EigenvalueDecomposition + to avoid infinite loop; + Thanks Frederic Devernay + * Updated unchecked type coding in Matrix read. + * Added serialization ID to Jama.Matrix and Decomposition classes. + * Cleaned up javadoc documentation to Jama.Matrix and Decomposition classes. +2005-07-13 Bruce Miller + * Version 1.0.2 Released. + +2005-07-12 Bruce Miller + * Although SVD sometimes fails on cases m < n, + the test code, apparently successfully, invokes + SVD to compute rank on an m + * Version 1.0.1 Released. + +2000-09-11 Bruce Miller + * Jama.Matrix print methods which create a NumberFormat, now set its + Locale to US so that the reader will recognize them even when the + default locale is not US. Similar change to Jama.test.TestMatrix. + (Thanks Ulrich Eberhardinger ) + +1998-08-05 The Jama Team + * Initial Version released (1.0.0) + diff --git a/Java/src/Jama/CholeskyDecomposition.java b/Java/src/Jama/CholeskyDecomposition.java new file mode 100644 index 00000000..57f4ccfb --- /dev/null +++ b/Java/src/Jama/CholeskyDecomposition.java @@ -0,0 +1,201 @@ +package Jama; + + /** Cholesky Decomposition. +

+ For a symmetric, positive definite matrix A, the Cholesky decomposition + is an lower triangular matrix L so that A = L*L'. +

+ If the matrix is not symmetric or positive definite, the constructor + returns a partial decomposition and sets an internal flag that may + be queried by the isSPD() method. + */ + +public class CholeskyDecomposition implements java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Array for internal storage of decomposition. + @serial internal array storage. + */ + private double[][] L; + + /** Row and column dimension (square matrix). + @serial matrix dimension. + */ + private int n; + + /** Symmetric and positive definite flag. + @serial is symmetric and positive definite flag. + */ + private boolean isspd; + +/* ------------------------ + Constructor + * ------------------------ */ + + /** Cholesky algorithm for symmetric and positive definite matrix. + Structure to access L and isspd flag. + @param Arg Square, symmetric matrix. + */ + + public CholeskyDecomposition (Matrix Arg) { + + + // Initialize. + double[][] A = Arg.getArray(); + n = Arg.getRowDimension(); + L = new double[n][n]; + isspd = (Arg.getColumnDimension() == n); + // Main loop. + for (int j = 0; j < n; j++) { + double[] Lrowj = L[j]; + double d = 0.0; + for (int k = 0; k < j; k++) { + double[] Lrowk = L[k]; + double s = 0.0; + for (int i = 0; i < k; i++) { + s += Lrowk[i]*Lrowj[i]; + } + Lrowj[k] = s = (A[j][k] - s)/L[k][k]; + d = d + s*s; + isspd = isspd & (A[k][j] == A[j][k]); + } + d = A[j][j] - d; + isspd = isspd & (d > 0.0); + L[j][j] = Math.sqrt(Math.max(d,0.0)); + for (int k = j+1; k < n; k++) { + L[j][k] = 0.0; + } + } + } + +/* ------------------------ + Temporary, experimental code. + * ------------------------ *\ + + \** Right Triangular Cholesky Decomposition. +

+ For a symmetric, positive definite matrix A, the Right Cholesky + decomposition is an upper triangular matrix R so that A = R'*R. + This constructor computes R with the Fortran inspired column oriented + algorithm used in LINPACK and MATLAB. In Java, we suspect a row oriented, + lower triangular decomposition is faster. We have temporarily included + this constructor here until timing experiments confirm this suspicion. + *\ + + \** Array for internal storage of right triangular decomposition. **\ + private transient double[][] R; + + \** Cholesky algorithm for symmetric and positive definite matrix. + @param A Square, symmetric matrix. + @param rightflag Actual value ignored. + @return Structure to access R and isspd flag. + *\ + + public CholeskyDecomposition (Matrix Arg, int rightflag) { + // Initialize. + double[][] A = Arg.getArray(); + n = Arg.getColumnDimension(); + R = new double[n][n]; + isspd = (Arg.getColumnDimension() == n); + // Main loop. + for (int j = 0; j < n; j++) { + double d = 0.0; + for (int k = 0; k < j; k++) { + double s = A[k][j]; + for (int i = 0; i < k; i++) { + s = s - R[i][k]*R[i][j]; + } + R[k][j] = s = s/R[k][k]; + d = d + s*s; + isspd = isspd & (A[k][j] == A[j][k]); + } + d = A[j][j] - d; + isspd = isspd & (d > 0.0); + R[j][j] = Math.sqrt(Math.max(d,0.0)); + for (int k = j+1; k < n; k++) { + R[k][j] = 0.0; + } + } + } + + \** Return upper triangular factor. + @return R + *\ + + public Matrix getR () { + return new Matrix(R,n,n); + } + +\* ------------------------ + End of temporary code. + * ------------------------ */ + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Is the matrix symmetric and positive definite? + @return true if A is symmetric and positive definite. + */ + + public boolean isSPD () { + return isspd; + } + + /** Return triangular factor. + @return L + */ + + public Matrix getL () { + return new Matrix(L,n,n); + } + + /** Solve A*X = B + @param B A Matrix with as many rows as A and any number of columns. + @return X so that L*L'*X = B + @exception IllegalArgumentException Matrix row dimensions must agree. + @exception RuntimeException Matrix is not symmetric positive definite. + */ + + public Matrix solve (Matrix B) { + if (B.getRowDimension() != n) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!isspd) { + throw new RuntimeException("Matrix is not symmetric positive definite."); + } + + // Copy right hand side. + double[][] X = B.getArrayCopy(); + int nx = B.getColumnDimension(); + + // Solve L*Y = B; + for (int k = 0; k < n; k++) { + for (int j = 0; j < nx; j++) { + for (int i = 0; i < k ; i++) { + X[k][j] -= X[i][j]*L[k][i]; + } + X[k][j] /= L[k][k]; + } + } + + // Solve L'*X = Y; + for (int k = n-1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + for (int i = k+1; i < n ; i++) { + X[k][j] -= X[i][j]*L[i][k]; + } + X[k][j] /= L[k][k]; + } + } + + + return new Matrix(X,n,nx); + } + private static final long serialVersionUID = 1; + +} + diff --git a/Java/src/Jama/EigenvalueDecomposition.java b/Java/src/Jama/EigenvalueDecomposition.java new file mode 100644 index 00000000..6657c3fa --- /dev/null +++ b/Java/src/Jama/EigenvalueDecomposition.java @@ -0,0 +1,956 @@ +package Jama; +import Jama.util.*; + +/** Eigenvalues and eigenvectors of a real matrix. +

+ If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is + diagonal and the eigenvector matrix V is orthogonal. + I.e. A = V.times(D.times(V.transpose())) and + V.times(V.transpose()) equals the identity matrix. +

+ If A is not symmetric, then the eigenvalue matrix D is block diagonal + with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, + lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The + columns of V represent the eigenvectors in the sense that A*V = V*D, + i.e. A.times(V) equals V.times(D). The matrix V may be badly + conditioned, or even singular, so the validity of the equation + A = V*D*inverse(V) depends upon V.cond(). +**/ + +public class EigenvalueDecomposition implements java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Row and column dimension (square matrix). + @serial matrix dimension. + */ + private int n; + + /** Symmetry flag. + @serial internal symmetry flag. + */ + private boolean issymmetric; + + /** Arrays for internal storage of eigenvalues. + @serial internal storage of eigenvalues. + */ + private double[] d, e; + + /** Array for internal storage of eigenvectors. + @serial internal storage of eigenvectors. + */ + private double[][] V; + + /** Array for internal storage of nonsymmetric Hessenberg form. + @serial internal storage of nonsymmetric Hessenberg form. + */ + private double[][] H; + + /** Working storage for nonsymmetric algorithm. + @serial working storage for nonsymmetric algorithm. + */ + private double[] ort; + +/* ------------------------ + Private Methods + * ------------------------ */ + + // Symmetric Householder reduction to tridiagonal form. + + private void tred2 () { + + // This is derived from the Algol procedures tred2 by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int j = 0; j < n; j++) { + d[j] = V[n-1][j]; + } + + // Householder reduction to tridiagonal form. + + for (int i = n-1; i > 0; i--) { + + // Scale to avoid under/overflow. + + double scale = 0.0; + double h = 0.0; + for (int k = 0; k < i; k++) { + scale = scale + Math.abs(d[k]); + } + if (scale == 0.0) { + e[i] = d[i-1]; + for (int j = 0; j < i; j++) { + d[j] = V[i-1][j]; + V[i][j] = 0.0; + V[j][i] = 0.0; + } + } else { + + // Generate Householder vector. + + for (int k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + double f = d[i-1]; + double g = Math.sqrt(h); + if (f > 0) { + g = -g; + } + e[i] = scale * g; + h = h - f * g; + d[i-1] = f - g; + for (int j = 0; j < i; j++) { + e[j] = 0.0; + } + + // Apply similarity transformation to remaining columns. + + for (int j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (int k = j+1; k <= i-1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + f = 0.0; + for (int j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + double hh = f / (h + h); + for (int j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + for (int j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (int k = j; k <= i-1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i-1][j]; + V[i][j] = 0.0; + } + } + d[i] = h; + } + + // Accumulate transformations. + + for (int i = 0; i < n-1; i++) { + V[n-1][i] = V[i][i]; + V[i][i] = 1.0; + double h = d[i+1]; + if (h != 0.0) { + for (int k = 0; k <= i; k++) { + d[k] = V[k][i+1] / h; + } + for (int j = 0; j <= i; j++) { + double g = 0.0; + for (int k = 0; k <= i; k++) { + g += V[k][i+1] * V[k][j]; + } + for (int k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + for (int k = 0; k <= i; k++) { + V[k][i+1] = 0.0; + } + } + for (int j = 0; j < n; j++) { + d[j] = V[n-1][j]; + V[n-1][j] = 0.0; + } + V[n-1][n-1] = 1.0; + e[0] = 0.0; + } + + // Symmetric tridiagonal QL algorithm. + + private void tql2 () { + + // This is derived from the Algol procedures tql2, by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int i = 1; i < n; i++) { + e[i-1] = e[i]; + } + e[n-1] = 0.0; + + double f = 0.0; + double tst1 = 0.0; + double eps = Math.pow(2.0,-52.0); + for (int l = 0; l < n; l++) { + + // Find small subdiagonal element + + tst1 = Math.max(tst1,Math.abs(d[l]) + Math.abs(e[l])); + int m = l; + while (m < n) { + if (Math.abs(e[m]) <= eps*tst1) { + break; + } + m++; + } + + // If m == l, d[l] is an eigenvalue, + // otherwise, iterate. + + if (m > l) { + int iter = 0; + do { + iter = iter + 1; // (Could check iteration count here.) + + // Compute implicit shift + + double g = d[l]; + double p = (d[l+1] - g) / (2.0 * e[l]); + double r = Maths.hypot(p,1.0); + if (p < 0) { + r = -r; + } + d[l] = e[l] / (p + r); + d[l+1] = e[l] * (p + r); + double dl1 = d[l+1]; + double h = g - d[l]; + for (int i = l+2; i < n; i++) { + d[i] -= h; + } + f = f + h; + + // Implicit QL transformation. + + p = d[m]; + double c = 1.0; + double c2 = c; + double c3 = c; + double el1 = e[l+1]; + double s = 0.0; + double s2 = 0.0; + for (int i = m-1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = Maths.hypot(p,e[i]); + e[i+1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i+1] = h + s * (c * g + s * d[i]); + + // Accumulate transformation. + + for (int k = 0; k < n; k++) { + h = V[k][i+1]; + V[k][i+1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + + // Check for convergence. + + } while (Math.abs(e[l]) > eps*tst1); + } + d[l] = d[l] + f; + e[l] = 0.0; + } + + // Sort eigenvalues and corresponding vectors. + + for (int i = 0; i < n-1; i++) { + int k = i; + double p = d[i]; + for (int j = i+1; j < n; j++) { + if (d[j] < p) { + k = j; + p = d[j]; + } + } + if (k != i) { + d[k] = d[i]; + d[i] = p; + for (int j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } + } + + // Nonsymmetric reduction to Hessenberg form. + + private void orthes () { + + // This is derived from the Algol procedures orthes and ortran, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutines in EISPACK. + + int low = 0; + int high = n-1; + + for (int m = low+1; m <= high-1; m++) { + + // Scale column. + + double scale = 0.0; + for (int i = m; i <= high; i++) { + scale = scale + Math.abs(H[i][m-1]); + } + if (scale != 0.0) { + + // Compute Householder transformation. + + double h = 0.0; + for (int i = high; i >= m; i--) { + ort[i] = H[i][m-1]/scale; + h += ort[i] * ort[i]; + } + double g = Math.sqrt(h); + if (ort[m] > 0) { + g = -g; + } + h = h - ort[m] * g; + ort[m] = ort[m] - g; + + // Apply Householder similarity transformation + // H = (I-u*u'/h)*H*(I-u*u')/h) + + for (int j = m; j < n; j++) { + double f = 0.0; + for (int i = high; i >= m; i--) { + f += ort[i]*H[i][j]; + } + f = f/h; + for (int i = m; i <= high; i++) { + H[i][j] -= f*ort[i]; + } + } + + for (int i = 0; i <= high; i++) { + double f = 0.0; + for (int j = high; j >= m; j--) { + f += ort[j]*H[i][j]; + } + f = f/h; + for (int j = m; j <= high; j++) { + H[i][j] -= f*ort[j]; + } + } + ort[m] = scale*ort[m]; + H[m][m-1] = scale*g; + } + } + + // Accumulate transformations (Algol's ortran). + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = (i == j ? 1.0 : 0.0); + } + } + + for (int m = high-1; m >= low+1; m--) { + if (H[m][m-1] != 0.0) { + for (int i = m+1; i <= high; i++) { + ort[i] = H[i][m-1]; + } + for (int j = m; j <= high; j++) { + double g = 0.0; + for (int i = m; i <= high; i++) { + g += ort[i] * V[i][j]; + } + // Double division avoids possible underflow + g = (g / ort[m]) / H[m][m-1]; + for (int i = m; i <= high; i++) { + V[i][j] += g * ort[i]; + } + } + } + } + } + + + // Complex scalar division. + + private transient double cdivr, cdivi; + private void cdiv(double xr, double xi, double yr, double yi) { + double r,d; + if (Math.abs(yr) > Math.abs(yi)) { + r = yi/yr; + d = yr + r*yi; + cdivr = (xr + r*xi)/d; + cdivi = (xi - r*xr)/d; + } else { + r = yr/yi; + d = yi + r*yr; + cdivr = (r*xr + xi)/d; + cdivi = (r*xi - xr)/d; + } + } + + + // Nonsymmetric reduction from Hessenberg to real Schur form. + + private void hqr2 () { + + // This is derived from the Algol procedure hqr2, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + // Initialize + + int nn = this.n; + int n = nn-1; + int low = 0; + int high = nn-1; + double eps = Math.pow(2.0,-52.0); + double exshift = 0.0; + double p=0,q=0,r=0,s=0,z=0,t,w,x,y; + + // Store roots isolated by balanc and compute matrix norm + + double norm = 0.0; + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + d[i] = H[i][i]; + e[i] = 0.0; + } + for (int j = Math.max(i-1,0); j < nn; j++) { + norm = norm + Math.abs(H[i][j]); + } + } + + // Outer loop over eigenvalue index + + int iter = 0; + while (n >= low) { + + // Look for single small sub-diagonal element + + int l = n; + while (l > low) { + s = Math.abs(H[l-1][l-1]) + Math.abs(H[l][l]); + if (s == 0.0) { + s = norm; + } + if (Math.abs(H[l][l-1]) < eps * s) { + break; + } + l--; + } + + // Check for convergence + // One root found + + if (l == n) { + H[n][n] = H[n][n] + exshift; + d[n] = H[n][n]; + e[n] = 0.0; + n--; + iter = 0; + + // Two roots found + + } else if (l == n-1) { + w = H[n][n-1] * H[n-1][n]; + p = (H[n-1][n-1] - H[n][n]) / 2.0; + q = p * p + w; + z = Math.sqrt(Math.abs(q)); + H[n][n] = H[n][n] + exshift; + H[n-1][n-1] = H[n-1][n-1] + exshift; + x = H[n][n]; + + // Real pair + + if (q >= 0) { + if (p >= 0) { + z = p + z; + } else { + z = p - z; + } + d[n-1] = x + z; + d[n] = d[n-1]; + if (z != 0.0) { + d[n] = x - w / z; + } + e[n-1] = 0.0; + e[n] = 0.0; + x = H[n][n-1]; + s = Math.abs(x) + Math.abs(z); + p = x / s; + q = z / s; + r = Math.sqrt(p * p+q * q); + p = p / r; + q = q / r; + + // Row modification + + for (int j = n-1; j < nn; j++) { + z = H[n-1][j]; + H[n-1][j] = q * z + p * H[n][j]; + H[n][j] = q * H[n][j] - p * z; + } + + // Column modification + + for (int i = 0; i <= n; i++) { + z = H[i][n-1]; + H[i][n-1] = q * z + p * H[i][n]; + H[i][n] = q * H[i][n] - p * z; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + z = V[i][n-1]; + V[i][n-1] = q * z + p * V[i][n]; + V[i][n] = q * V[i][n] - p * z; + } + + // Complex pair + + } else { + d[n-1] = x + p; + d[n] = x + p; + e[n-1] = z; + e[n] = -z; + } + n = n - 2; + iter = 0; + + // No convergence yet + + } else { + + // Form shift + + x = H[n][n]; + y = 0.0; + w = 0.0; + if (l < n) { + y = H[n-1][n-1]; + w = H[n][n-1] * H[n-1][n]; + } + + // Wilkinson's original ad hoc shift + + if (iter == 10) { + exshift += x; + for (int i = low; i <= n; i++) { + H[i][i] -= x; + } + s = Math.abs(H[n][n-1]) + Math.abs(H[n-1][n-2]); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + // MATLAB's new ad hoc shift + + if (iter == 30) { + s = (y - x) / 2.0; + s = s * s + w; + if (s > 0) { + s = Math.sqrt(s); + if (y < x) { + s = -s; + } + s = x - w / ((y - x) / 2.0 + s); + for (int i = low; i <= n; i++) { + H[i][i] -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; // (Could check iteration count here.) + + // Look for two consecutive small sub-diagonal elements + + int m = n-2; + while (m >= l) { + z = H[m][m]; + r = x - z; + s = y - z; + p = (r * s - w) / H[m+1][m] + H[m][m+1]; + q = H[m+1][m+1] - z - r - s; + r = H[m+2][m+1]; + s = Math.abs(p) + Math.abs(q) + Math.abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m == l) { + break; + } + if (Math.abs(H[m][m-1]) * (Math.abs(q) + Math.abs(r)) < + eps * (Math.abs(p) * (Math.abs(H[m-1][m-1]) + Math.abs(z) + + Math.abs(H[m+1][m+1])))) { + break; + } + m--; + } + + for (int i = m+2; i <= n; i++) { + H[i][i-2] = 0.0; + if (i > m+2) { + H[i][i-3] = 0.0; + } + } + + // Double QR step involving rows l:n and columns m:n + + + for (int k = m; k <= n-1; k++) { + boolean notlast = (k != n-1); + if (k != m) { + p = H[k][k-1]; + q = H[k+1][k-1]; + r = (notlast ? H[k+2][k-1] : 0.0); + x = Math.abs(p) + Math.abs(q) + Math.abs(r); + if (x == 0.0) { + continue; + } + p = p / x; + q = q / x; + r = r / x; + } + + s = Math.sqrt(p * p + q * q + r * r); + if (p < 0) { + s = -s; + } + if (s != 0) { + if (k != m) { + H[k][k-1] = -s * x; + } else if (l != m) { + H[k][k-1] = -H[k][k-1]; + } + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + // Row modification + + for (int j = k; j < nn; j++) { + p = H[k][j] + q * H[k+1][j]; + if (notlast) { + p = p + r * H[k+2][j]; + H[k+2][j] = H[k+2][j] - p * z; + } + H[k][j] = H[k][j] - p * x; + H[k+1][j] = H[k+1][j] - p * y; + } + + // Column modification + + for (int i = 0; i <= Math.min(n,k+3); i++) { + p = x * H[i][k] + y * H[i][k+1]; + if (notlast) { + p = p + z * H[i][k+2]; + H[i][k+2] = H[i][k+2] - p * r; + } + H[i][k] = H[i][k] - p; + H[i][k+1] = H[i][k+1] - p * q; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + p = x * V[i][k] + y * V[i][k+1]; + if (notlast) { + p = p + z * V[i][k+2]; + V[i][k+2] = V[i][k+2] - p * r; + } + V[i][k] = V[i][k] - p; + V[i][k+1] = V[i][k+1] - p * q; + } + } // (s != 0) + } // k loop + } // check convergence + } // while (n >= low) + + // Backsubstitute to find vectors of upper triangular form + + if (norm == 0.0) { + return; + } + + for (n = nn-1; n >= 0; n--) { + p = d[n]; + q = e[n]; + + // Real vector + + if (q == 0) { + int l = n; + H[n][n] = 1.0; + for (int i = n-1; i >= 0; i--) { + w = H[i][i] - p; + r = 0.0; + for (int j = l; j <= n; j++) { + r = r + H[i][j] * H[j][n]; + } + if (e[i] < 0.0) { + z = w; + s = r; + } else { + l = i; + if (e[i] == 0.0) { + if (w != 0.0) { + H[i][n] = -r / w; + } else { + H[i][n] = -r / (eps * norm); + } + + // Solve real equations + + } else { + x = H[i][i+1]; + y = H[i+1][i]; + q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; + t = (x * s - z * r) / q; + H[i][n] = t; + if (Math.abs(x) > Math.abs(z)) { + H[i+1][n] = (-r - w * t) / x; + } else { + H[i+1][n] = (-s - y * t) / z; + } + } + + // Overflow control + + t = Math.abs(H[i][n]); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n] = H[j][n] / t; + } + } + } + } + + // Complex vector + + } else if (q < 0) { + int l = n-1; + + // Last vector component imaginary so matrix is triangular + + if (Math.abs(H[n][n-1]) > Math.abs(H[n-1][n])) { + H[n-1][n-1] = q / H[n][n-1]; + H[n-1][n] = -(H[n][n] - p) / H[n][n-1]; + } else { + cdiv(0.0,-H[n-1][n],H[n-1][n-1]-p,q); + H[n-1][n-1] = cdivr; + H[n-1][n] = cdivi; + } + H[n][n-1] = 0.0; + H[n][n] = 1.0; + for (int i = n-2; i >= 0; i--) { + double ra,sa,vr,vi; + ra = 0.0; + sa = 0.0; + for (int j = l; j <= n; j++) { + ra = ra + H[i][j] * H[j][n-1]; + sa = sa + H[i][j] * H[j][n]; + } + w = H[i][i] - p; + + if (e[i] < 0.0) { + z = w; + r = ra; + s = sa; + } else { + l = i; + if (e[i] == 0) { + cdiv(-ra,-sa,w,q); + H[i][n-1] = cdivr; + H[i][n] = cdivi; + } else { + + // Solve complex equations + + x = H[i][i+1]; + y = H[i+1][i]; + vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; + vi = (d[i] - p) * 2.0 * q; + if (vr == 0.0 & vi == 0.0) { + vr = eps * norm * (Math.abs(w) + Math.abs(q) + + Math.abs(x) + Math.abs(y) + Math.abs(z)); + } + cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); + H[i][n-1] = cdivr; + H[i][n] = cdivi; + if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) { + H[i+1][n-1] = (-ra - w * H[i][n-1] + q * H[i][n]) / x; + H[i+1][n] = (-sa - w * H[i][n] - q * H[i][n-1]) / x; + } else { + cdiv(-r-y*H[i][n-1],-s-y*H[i][n],z,q); + H[i+1][n-1] = cdivr; + H[i+1][n] = cdivi; + } + } + + // Overflow control + + t = Math.max(Math.abs(H[i][n-1]),Math.abs(H[i][n])); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n-1] = H[j][n-1] / t; + H[j][n] = H[j][n] / t; + } + } + } + } + } + } + + // Vectors of isolated roots + + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + for (int j = i; j < nn; j++) { + V[i][j] = H[i][j]; + } + } + } + + // Back transformation to get eigenvectors of original matrix + + for (int j = nn-1; j >= low; j--) { + for (int i = low; i <= high; i++) { + z = 0.0; + for (int k = low; k <= Math.min(j,high); k++) { + z = z + V[i][k] * H[k][j]; + } + V[i][j] = z; + } + } + } + + +/* ------------------------ + Constructor + * ------------------------ */ + + /** Check for symmetry, then construct the eigenvalue decomposition + Structure to access D and V. + @param Arg Square matrix + */ + + public EigenvalueDecomposition (Matrix Arg) { + double[][] A = Arg.getArray(); + n = Arg.getColumnDimension(); + V = new double[n][n]; + d = new double[n]; + e = new double[n]; + + issymmetric = true; + for (int j = 0; (j < n) & issymmetric; j++) { + for (int i = 0; (i < n) & issymmetric; i++) { + issymmetric = (A[i][j] == A[j][i]); + } + } + + if (issymmetric) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = A[i][j]; + } + } + + // Tridiagonalize. + tred2(); + + // Diagonalize. + tql2(); + + } else { + H = new double[n][n]; + ort = new double[n]; + + for (int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + H[i][j] = A[i][j]; + } + } + + // Reduce to Hessenberg form. + orthes(); + + // Reduce Hessenberg to real Schur form. + hqr2(); + } + } + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Return the eigenvector matrix + @return V + */ + + public Matrix getV () { + return new Matrix(V,n,n); + } + + /** Return the real parts of the eigenvalues + @return real(diag(D)) + */ + + public double[] getRealEigenvalues () { + return d; + } + + /** Return the imaginary parts of the eigenvalues + @return imag(diag(D)) + */ + + public double[] getImagEigenvalues () { + return e; + } + + /** Return the block diagonal eigenvalue matrix + @return D + */ + + public Matrix getD () { + Matrix X = new Matrix(n,n); + double[][] D = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + D[i][j] = 0.0; + } + D[i][i] = d[i]; + if (e[i] > 0) { + D[i][i+1] = e[i]; + } else if (e[i] < 0) { + D[i][i-1] = e[i]; + } + } + return X; + } + private static final long serialVersionUID = 1; +} diff --git a/Java/src/Jama/LUDecomposition.java b/Java/src/Jama/LUDecomposition.java new file mode 100644 index 00000000..dc43dc78 --- /dev/null +++ b/Java/src/Jama/LUDecomposition.java @@ -0,0 +1,312 @@ +package Jama; + + /** LU Decomposition. +

+ For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n + unit lower triangular matrix L, an n-by-n upper triangular matrix U, + and a permutation vector piv of length m so that A(piv,:) = L*U. + If m < n, then L is m-by-m and U is m-by-n. +

+ The LU decompostion with pivoting always exists, even if the matrix is + singular, so the constructor will never fail. The primary use of the + LU decomposition is in the solution of square systems of simultaneous + linear equations. This will fail if isNonsingular() returns false. + */ + +public class LUDecomposition implements java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Array for internal storage of decomposition. + @serial internal array storage. + */ + private double[][] LU; + + /** Row and column dimensions, and pivot sign. + @serial column dimension. + @serial row dimension. + @serial pivot sign. + */ + private int m, n, pivsign; + + /** Internal storage of pivot vector. + @serial pivot vector. + */ + private int[] piv; + +/* ------------------------ + Constructor + * ------------------------ */ + + /** LU Decomposition + Structure to access L, U and piv. + @param A Rectangular matrix + */ + + public LUDecomposition (Matrix A) { + + // Use a "left-looking", dot-product, Crout/Doolittle algorithm. + + LU = A.getArrayCopy(); + m = A.getRowDimension(); + n = A.getColumnDimension(); + piv = new int[m]; + for (int i = 0; i < m; i++) { + piv[i] = i; + } + pivsign = 1; + double[] LUrowi; + double[] LUcolj = new double[m]; + + // Outer loop. + + for (int j = 0; j < n; j++) { + + // Make a copy of the j-th column to localize references. + + for (int i = 0; i < m; i++) { + LUcolj[i] = LU[i][j]; + } + + // Apply previous transformations. + + for (int i = 0; i < m; i++) { + LUrowi = LU[i]; + + // Most of the time is spent in the following dot product. + + int kmax = Math.min(i,j); + double s = 0.0; + for (int k = 0; k < kmax; k++) { + s += LUrowi[k]*LUcolj[k]; + } + + LUrowi[j] = LUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + + int p = j; + for (int i = j+1; i < m; i++) { + if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) { + p = i; + } + } + if (p != j) { + for (int k = 0; k < n; k++) { + double t = LU[p][k]; LU[p][k] = LU[j][k]; LU[j][k] = t; + } + int k = piv[p]; piv[p] = piv[j]; piv[j] = k; + pivsign = -pivsign; + } + + // Compute multipliers. + + if (j < m & LU[j][j] != 0.0) { + for (int i = j+1; i < m; i++) { + LU[i][j] /= LU[j][j]; + } + } + } + } + +/* ------------------------ + Temporary, experimental code. + ------------------------ *\ + + \** LU Decomposition, computed by Gaussian elimination. +

+ This constructor computes L and U with the "daxpy"-based elimination + algorithm used in LINPACK and MATLAB. In Java, we suspect the dot-product, + Crout algorithm will be faster. We have temporarily included this + constructor until timing experiments confirm this suspicion. +

+ @param A Rectangular matrix + @param linpackflag Use Gaussian elimination. Actual value ignored. + @return Structure to access L, U and piv. + *\ + + public LUDecomposition (Matrix A, int linpackflag) { + // Initialize. + LU = A.getArrayCopy(); + m = A.getRowDimension(); + n = A.getColumnDimension(); + piv = new int[m]; + for (int i = 0; i < m; i++) { + piv[i] = i; + } + pivsign = 1; + // Main loop. + for (int k = 0; k < n; k++) { + // Find pivot. + int p = k; + for (int i = k+1; i < m; i++) { + if (Math.abs(LU[i][k]) > Math.abs(LU[p][k])) { + p = i; + } + } + // Exchange if necessary. + if (p != k) { + for (int j = 0; j < n; j++) { + double t = LU[p][j]; LU[p][j] = LU[k][j]; LU[k][j] = t; + } + int t = piv[p]; piv[p] = piv[k]; piv[k] = t; + pivsign = -pivsign; + } + // Compute multipliers and eliminate k-th column. + if (LU[k][k] != 0.0) { + for (int i = k+1; i < m; i++) { + LU[i][k] /= LU[k][k]; + for (int j = k+1; j < n; j++) { + LU[i][j] -= LU[i][k]*LU[k][j]; + } + } + } + } + } + +\* ------------------------ + End of temporary code. + * ------------------------ */ + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Is the matrix nonsingular? + @return true if U, and hence A, is nonsingular. + */ + + public boolean isNonsingular () { + for (int j = 0; j < n; j++) { + if (LU[j][j] == 0) + return false; + } + return true; + } + + /** Return lower triangular factor + @return L + */ + + public Matrix getL () { + Matrix X = new Matrix(m,n); + double[][] L = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i > j) { + L[i][j] = LU[i][j]; + } else if (i == j) { + L[i][j] = 1.0; + } else { + L[i][j] = 0.0; + } + } + } + return X; + } + + /** Return upper triangular factor + @return U + */ + + public Matrix getU () { + Matrix X = new Matrix(n,n); + double[][] U = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i <= j) { + U[i][j] = LU[i][j]; + } else { + U[i][j] = 0.0; + } + } + } + return X; + } + + /** Return pivot permutation vector + @return piv + */ + + public int[] getPivot () { + int[] p = new int[m]; + for (int i = 0; i < m; i++) { + p[i] = piv[i]; + } + return p; + } + + /** Return pivot permutation vector as a one-dimensional double array + @return (double) piv + */ + + public double[] getDoublePivot () { + double[] vals = new double[m]; + for (int i = 0; i < m; i++) { + vals[i] = (double) piv[i]; + } + return vals; + } + + /** Determinant + @return det(A) + @exception IllegalArgumentException Matrix must be square + */ + + public double det () { + if (m != n) { + throw new IllegalArgumentException("Matrix must be square."); + } + double d = (double) pivsign; + for (int j = 0; j < n; j++) { + d *= LU[j][j]; + } + return d; + } + + /** Solve A*X = B + @param B A Matrix with as many rows as A and any number of columns. + @return X so that L*U*X = B(piv,:) + @exception IllegalArgumentException Matrix row dimensions must agree. + @exception RuntimeException Matrix is singular. + */ + + public Matrix solve (Matrix B) { + if (B.getRowDimension() != m) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!this.isNonsingular()) { + throw new RuntimeException("Matrix is singular."); + } + + // Copy right hand side with pivoting + int nx = B.getColumnDimension(); + Matrix Xmat = B.getMatrix(piv,0,nx-1); + double[][] X = Xmat.getArray(); + + // Solve L*Y = B(piv,:) + for (int k = 0; k < n; k++) { + for (int i = k+1; i < n; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j]*LU[i][k]; + } + } + } + // Solve U*X = Y; + for (int k = n-1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + X[k][j] /= LU[k][k]; + } + for (int i = 0; i < k; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j]*LU[i][k]; + } + } + } + return Xmat; + } + private static final long serialVersionUID = 1; +} diff --git a/Java/src/Jama/Matrix.java b/Java/src/Jama/Matrix.java new file mode 100644 index 00000000..8b113877 --- /dev/null +++ b/Java/src/Jama/Matrix.java @@ -0,0 +1,1046 @@ +package Jama; + +import java.text.NumberFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.text.FieldPosition; +import java.io.PrintWriter; +import java.io.BufferedReader; +import java.io.StreamTokenizer; +import Jama.util.*; + +/** + Jama = Java Matrix class. +

+ The Java Matrix Class provides the fundamental operations of numerical + linear algebra. Various constructors create Matrices from two dimensional + arrays of double precision floating point numbers. Various "gets" and + "sets" provide access to submatrices and matrix elements. Several methods + implement basic matrix arithmetic, including matrix addition and + multiplication, matrix norms, and element-by-element array operations. + Methods for reading and printing matrices are also included. All the + operations in this version of the Matrix Class involve real matrices. + Complex matrices may be handled in a future version. +

+ Five fundamental matrix decompositions, which consist of pairs or triples + of matrices, permutation vectors, and the like, produce results in five + decomposition classes. These decompositions are accessed by the Matrix + class to compute solutions of simultaneous linear equations, determinants, + inverses and other matrix functions. The five decompositions are: +

    +
  • Cholesky Decomposition of symmetric, positive definite matrices. +
  • LU Decomposition of rectangular matrices. +
  • QR Decomposition of rectangular matrices. +
  • Singular Value Decomposition of rectangular matrices. +
  • Eigenvalue Decomposition of both symmetric and nonsymmetric square matrices. +
+
+
Example of use:
+

+

Solve a linear system A x = b and compute the residual norm, ||b - A x||. +

+      double[][] vals = {{1.,2.,3},{4.,5.,6.},{7.,8.,10.}};
+      Matrix A = new Matrix(vals);
+      Matrix b = Matrix.random(3,1);
+      Matrix x = A.solve(b);
+      Matrix r = A.times(x).minus(b);
+      double rnorm = r.normInf();
+
+
+ +@author The MathWorks, Inc. and the National Institute of Standards and Technology. +@version 5 August 1998 +*/ + +public class Matrix implements Cloneable, java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Array for internal storage of elements. + @serial internal array storage. + */ + private double[][] A; + + /** Row and column dimensions. + @serial row dimension. + @serial column dimension. + */ + private int m, n; + +/* ------------------------ + Constructors + * ------------------------ */ + + /** Construct an m-by-n matrix of zeros. + @param m Number of rows. + @param n Number of colums. + */ + + public Matrix (int m, int n) { + this.m = m; + this.n = n; + A = new double[m][n]; + } + + /** Construct an m-by-n constant matrix. + @param m Number of rows. + @param n Number of colums. + @param s Fill the matrix with this scalar value. + */ + + public Matrix (int m, int n, double s) { + this.m = m; + this.n = n; + A = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = s; + } + } + } + + /** Construct a matrix from a 2-D array. + @param A Two-dimensional array of doubles. + @exception IllegalArgumentException All rows must have the same length + @see #constructWithCopy + */ + + public Matrix (double[][] A) { + m = A.length; + n = A[0].length; + for (int i = 0; i < m; i++) { + if (A[i].length != n) { + throw new IllegalArgumentException("All rows must have the same length."); + } + } + this.A = A; + } + + /** Construct a matrix quickly without checking arguments. + @param A Two-dimensional array of doubles. + @param m Number of rows. + @param n Number of colums. + */ + + public Matrix (double[][] A, int m, int n) { + this.A = A; + this.m = m; + this.n = n; + } + + /** Construct a matrix from a one-dimensional packed array + @param vals One-dimensional array of doubles, packed by columns (ala Fortran). + @param m Number of rows. + @exception IllegalArgumentException Array length must be a multiple of m. + */ + + public Matrix (double vals[], int m) { + this.m = m; + n = (m != 0 ? vals.length/m : 0); + if (m*n != vals.length) { + throw new IllegalArgumentException("Array length must be a multiple of m."); + } + A = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = vals[i+j*m]; + } + } + } + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Construct a matrix from a copy of a 2-D array. + @param A Two-dimensional array of doubles. + @exception IllegalArgumentException All rows must have the same length + */ + + public static Matrix constructWithCopy(double[][] A) { + int m = A.length; + int n = A[0].length; + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + if (A[i].length != n) { + throw new IllegalArgumentException + ("All rows must have the same length."); + } + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return X; + } + + /** Make a deep copy of a matrix + */ + + public Matrix copy () { + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return X; + } + + /** Clone the Matrix object. + */ + + public Object clone () { + return this.copy(); + } + + /** Access the internal two-dimensional array. + @return Pointer to the two-dimensional array of matrix elements. + */ + + public double[][] getArray () { + return A; + } + + /** Copy the internal two-dimensional array. + @return Two-dimensional array copy of matrix elements. + */ + + public double[][] getArrayCopy () { + double[][] C = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return C; + } + + /** Make a one-dimensional column packed copy of the internal array. + @return Matrix elements packed in a one-dimensional array by columns. + */ + + public double[] getColumnPackedCopy () { + double[] vals = new double[m*n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + vals[i+j*m] = A[i][j]; + } + } + return vals; + } + + /** Make a one-dimensional row packed copy of the internal array. + @return Matrix elements packed in a one-dimensional array by rows. + */ + + public double[] getRowPackedCopy () { + double[] vals = new double[m*n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + vals[i*n+j] = A[i][j]; + } + } + return vals; + } + + /** Get row dimension. + @return m, the number of rows. + */ + + public int getRowDimension () { + return m; + } + + /** Get column dimension. + @return n, the number of columns. + */ + + public int getColumnDimension () { + return n; + } + + /** Get a single element. + @param i Row index. + @param j Column index. + @return A(i,j) + @exception ArrayIndexOutOfBoundsException + */ + + public double get (int i, int j) { + return A[i][j]; + } + + /** Get a submatrix. + @param i0 Initial row index + @param i1 Final row index + @param j0 Initial column index + @param j1 Final column index + @return A(i0:i1,j0:j1) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public Matrix getMatrix (int i0, int i1, int j0, int j1) { + Matrix X = new Matrix(i1-i0+1,j1-j0+1); + double[][] B = X.getArray(); + try { + for (int i = i0; i <= i1; i++) { + for (int j = j0; j <= j1; j++) { + B[i-i0][j-j0] = A[i][j]; + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** Get a submatrix. + @param r Array of row indices. + @param c Array of column indices. + @return A(r(:),c(:)) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public Matrix getMatrix (int[] r, int[] c) { + Matrix X = new Matrix(r.length,c.length); + double[][] B = X.getArray(); + try { + for (int i = 0; i < r.length; i++) { + for (int j = 0; j < c.length; j++) { + B[i][j] = A[r[i]][c[j]]; + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** Get a submatrix. + @param i0 Initial row index + @param i1 Final row index + @param c Array of column indices. + @return A(i0:i1,c(:)) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public Matrix getMatrix (int i0, int i1, int[] c) { + Matrix X = new Matrix(i1-i0+1,c.length); + double[][] B = X.getArray(); + try { + for (int i = i0; i <= i1; i++) { + for (int j = 0; j < c.length; j++) { + B[i-i0][j] = A[i][c[j]]; + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** Get a submatrix. + @param r Array of row indices. + @param j0 Initial column index + @param j1 Final column index + @return A(r(:),j0:j1) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public Matrix getMatrix (int[] r, int j0, int j1) { + Matrix X = new Matrix(r.length,j1-j0+1); + double[][] B = X.getArray(); + try { + for (int i = 0; i < r.length; i++) { + for (int j = j0; j <= j1; j++) { + B[i][j-j0] = A[r[i]][j]; + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** Set a single element. + @param i Row index. + @param j Column index. + @param s A(i,j). + @exception ArrayIndexOutOfBoundsException + */ + + public void set (int i, int j, double s) { + A[i][j] = s; + } + + /** Set a submatrix. + @param i0 Initial row index + @param i1 Final row index + @param j0 Initial column index + @param j1 Final column index + @param X A(i0:i1,j0:j1) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public void setMatrix (int i0, int i1, int j0, int j1, Matrix X) { + try { + for (int i = i0; i <= i1; i++) { + for (int j = j0; j <= j1; j++) { + A[i][j] = X.get(i-i0,j-j0); + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** Set a submatrix. + @param r Array of row indices. + @param c Array of column indices. + @param X A(r(:),c(:)) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public void setMatrix (int[] r, int[] c, Matrix X) { + try { + for (int i = 0; i < r.length; i++) { + for (int j = 0; j < c.length; j++) { + A[r[i]][c[j]] = X.get(i,j); + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** Set a submatrix. + @param r Array of row indices. + @param j0 Initial column index + @param j1 Final column index + @param X A(r(:),j0:j1) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public void setMatrix (int[] r, int j0, int j1, Matrix X) { + try { + for (int i = 0; i < r.length; i++) { + for (int j = j0; j <= j1; j++) { + A[r[i]][j] = X.get(i,j-j0); + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** Set a submatrix. + @param i0 Initial row index + @param i1 Final row index + @param c Array of column indices. + @param X A(i0:i1,c(:)) + @exception ArrayIndexOutOfBoundsException Submatrix indices + */ + + public void setMatrix (int i0, int i1, int[] c, Matrix X) { + try { + for (int i = i0; i <= i1; i++) { + for (int j = 0; j < c.length; j++) { + A[i][c[j]] = X.get(i-i0,j); + } + } + } catch(ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** Matrix transpose. + @return A' + */ + + public Matrix transpose () { + Matrix X = new Matrix(n,m); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[j][i] = A[i][j]; + } + } + return X; + } + + /** One norm + @return maximum column sum. + */ + + public double norm1 () { + double f = 0; + for (int j = 0; j < n; j++) { + double s = 0; + for (int i = 0; i < m; i++) { + s += Math.abs(A[i][j]); + } + f = Math.max(f,s); + } + return f; + } + + /** Two norm + @return maximum singular value. + */ + + public double norm2 () { + return (new SingularValueDecomposition(this).norm2()); + } + + /** Infinity norm + @return maximum row sum. + */ + + public double normInf () { + double f = 0; + for (int i = 0; i < m; i++) { + double s = 0; + for (int j = 0; j < n; j++) { + s += Math.abs(A[i][j]); + } + f = Math.max(f,s); + } + return f; + } + + /** Frobenius norm + @return sqrt of sum of squares of all elements. + */ + + public double normF () { + double f = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + f = Maths.hypot(f,A[i][j]); + } + } + return f; + } + + /** Unary minus + @return -A + */ + + public Matrix uminus () { + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = -A[i][j]; + } + } + return X; + } + + /** C = A + B + @param B another matrix + @return A + B + */ + + public Matrix plus (Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] + B.A[i][j]; + } + } + return X; + } + + /** A = A + B + @param B another matrix + @return A + B + */ + + public Matrix plusEquals (Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] + B.A[i][j]; + } + } + return this; + } + + /** C = A - B + @param B another matrix + @return A - B + */ + + public Matrix minus (Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] - B.A[i][j]; + } + } + return X; + } + + /** A = A - B + @param B another matrix + @return A - B + */ + + public Matrix minusEquals (Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] - B.A[i][j]; + } + } + return this; + } + + /** Element-by-element multiplication, C = A.*B + @param B another matrix + @return A.*B + */ + + public Matrix arrayTimes (Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] * B.A[i][j]; + } + } + return X; + } + + /** Element-by-element multiplication in place, A = A.*B + @param B another matrix + @return A.*B + */ + + public Matrix arrayTimesEquals (Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] * B.A[i][j]; + } + } + return this; + } + + /** Element-by-element right division, C = A./B + @param B another matrix + @return A./B + */ + + public Matrix arrayRightDivide (Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] / B.A[i][j]; + } + } + return X; + } + + /** Element-by-element right division in place, A = A./B + @param B another matrix + @return A./B + */ + + public Matrix arrayRightDivideEquals (Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] / B.A[i][j]; + } + } + return this; + } + + /** Element-by-element left division, C = A.\B + @param B another matrix + @return A.\B + */ + + public Matrix arrayLeftDivide (Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = B.A[i][j] / A[i][j]; + } + } + return X; + } + + /** Element-by-element left division in place, A = A.\B + @param B another matrix + @return A.\B + */ + + public Matrix arrayLeftDivideEquals (Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = B.A[i][j] / A[i][j]; + } + } + return this; + } + + /** Multiply a matrix by a scalar, C = s*A + @param s scalar + @return s*A + */ + + public Matrix times (double s) { + Matrix X = new Matrix(m,n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = s*A[i][j]; + } + } + return X; + } + + /** Multiply a matrix by a scalar in place, A = s*A + @param s scalar + @return replace A by s*A + */ + + public Matrix timesEquals (double s) { + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = s*A[i][j]; + } + } + return this; + } + + /** Linear algebraic matrix multiplication, A * B + @param B another matrix + @return Matrix product, A * B + @exception IllegalArgumentException Matrix inner dimensions must agree. + */ + + public Matrix times (Matrix B) { + if (B.m != n) { + throw new IllegalArgumentException("Matrix inner dimensions must agree."); + } + Matrix X = new Matrix(m,B.n); + double[][] C = X.getArray(); + double[] Bcolj = new double[n]; + for (int j = 0; j < B.n; j++) { + for (int k = 0; k < n; k++) { + Bcolj[k] = B.A[k][j]; + } + for (int i = 0; i < m; i++) { + double[] Arowi = A[i]; + double s = 0; + for (int k = 0; k < n; k++) { + s += Arowi[k]*Bcolj[k]; + } + C[i][j] = s; + } + } + return X; + } + + /** LU Decomposition + @return LUDecomposition + @see LUDecomposition + */ + + public LUDecomposition lu () { + return new LUDecomposition(this); + } + + /** QR Decomposition + @return QRDecomposition + @see QRDecomposition + */ + + public QRDecomposition qr () { + return new QRDecomposition(this); + } + + /** Cholesky Decomposition + @return CholeskyDecomposition + @see CholeskyDecomposition + */ + + public CholeskyDecomposition chol () { + return new CholeskyDecomposition(this); + } + + /** Singular Value Decomposition + @return SingularValueDecomposition + @see SingularValueDecomposition + */ + + public SingularValueDecomposition svd () { + return new SingularValueDecomposition(this); + } + + /** Eigenvalue Decomposition + @return EigenvalueDecomposition + @see EigenvalueDecomposition + */ + + public EigenvalueDecomposition eig () { + return new EigenvalueDecomposition(this); + } + + /** Solve A*X = B + @param B right hand side + @return solution if A is square, least squares solution otherwise + */ + + public Matrix solve (Matrix B) { + return (m == n ? (new LUDecomposition(this)).solve(B) : + (new QRDecomposition(this)).solve(B)); + } + + /** Solve X*A = B, which is also A'*X' = B' + @param B right hand side + @return solution if A is square, least squares solution otherwise. + */ + + public Matrix solveTranspose (Matrix B) { + return transpose().solve(B.transpose()); + } + + /** Matrix inverse or pseudoinverse + @return inverse(A) if A is square, pseudoinverse otherwise. + */ + + public Matrix inverse () { + return solve(identity(m,m)); + } + + /** Matrix determinant + @return determinant + */ + + public double det () { + return new LUDecomposition(this).det(); + } + + /** Matrix rank + @return effective numerical rank, obtained from SVD. + */ + + public int rank () { + return new SingularValueDecomposition(this).rank(); + } + + /** Matrix condition (2 norm) + @return ratio of largest to smallest singular value. + */ + + public double cond () { + return new SingularValueDecomposition(this).cond(); + } + + /** Matrix trace. + @return sum of the diagonal elements. + */ + + public double trace () { + double t = 0; + for (int i = 0; i < Math.min(m,n); i++) { + t += A[i][i]; + } + return t; + } + + /** Generate matrix with random elements + @param m Number of rows. + @param n Number of colums. + @return An m-by-n matrix with uniformly distributed random elements. + */ + + public static Matrix random (int m, int n) { + Matrix A = new Matrix(m,n); + double[][] X = A.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + X[i][j] = Math.random(); + } + } + return A; + } + + /** Generate identity matrix + @param m Number of rows. + @param n Number of colums. + @return An m-by-n matrix with ones on the diagonal and zeros elsewhere. + */ + + public static Matrix identity (int m, int n) { + Matrix A = new Matrix(m,n); + double[][] X = A.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + X[i][j] = (i == j ? 1.0 : 0.0); + } + } + return A; + } + + + /** Print the matrix to stdout. Line the elements up in columns + * with a Fortran-like 'Fw.d' style format. + @param w Column width. + @param d Number of digits after the decimal. + */ + + public void print (int w, int d) { + print(new PrintWriter(System.out,true),w,d); } + + /** Print the matrix to the output stream. Line the elements up in + * columns with a Fortran-like 'Fw.d' style format. + @param output Output stream. + @param w Column width. + @param d Number of digits after the decimal. + */ + + public void print (PrintWriter output, int w, int d) { + DecimalFormat format = new DecimalFormat(); + format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); + format.setMinimumIntegerDigits(1); + format.setMaximumFractionDigits(d); + format.setMinimumFractionDigits(d); + format.setGroupingUsed(false); + print(output,format,w+2); + } + + /** Print the matrix to stdout. Line the elements up in columns. + * Use the format object, and right justify within columns of width + * characters. + * Note that is the matrix is to be read back in, you probably will want + * to use a NumberFormat that is set to US Locale. + @param format A Formatting object for individual elements. + @param width Field width for each column. + @see java.text.DecimalFormat#setDecimalFormatSymbols + */ + + public void print (NumberFormat format, int width) { + print(new PrintWriter(System.out,true),format,width); } + + // DecimalFormat is a little disappointing coming from Fortran or C's printf. + // Since it doesn't pad on the left, the elements will come out different + // widths. Consequently, we'll pass the desired column width in as an + // argument and do the extra padding ourselves. + + /** Print the matrix to the output stream. Line the elements up in columns. + * Use the format object, and right justify within columns of width + * characters. + * Note that is the matrix is to be read back in, you probably will want + * to use a NumberFormat that is set to US Locale. + @param output the output stream. + @param format A formatting object to format the matrix elements + @param width Column width. + @see java.text.DecimalFormat#setDecimalFormatSymbols + */ + + public void print (PrintWriter output, NumberFormat format, int width) { + output.println(); // start on new line. + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + String s = format.format(A[i][j]); // format the number + int padding = Math.max(1,width-s.length()); // At _least_ 1 space + for (int k = 0; k < padding; k++) + output.print(' '); + output.print(s); + } + output.println(); + } + output.println(); // end with blank line. + } + + /** Read a matrix from a stream. The format is the same the print method, + * so printed matrices can be read back in (provided they were printed using + * US Locale). Elements are separated by + * whitespace, all the elements for each row appear on a single line, + * the last row is followed by a blank line. + @param input the input stream. + */ + + public static Matrix read (BufferedReader input) throws java.io.IOException { + StreamTokenizer tokenizer= new StreamTokenizer(input); + + // Although StreamTokenizer will parse numbers, it doesn't recognize + // scientific notation (E or D); however, Double.valueOf does. + // The strategy here is to disable StreamTokenizer's number parsing. + // We'll only get whitespace delimited words, EOL's and EOF's. + // These words should all be numbers, for Double.valueOf to parse. + + tokenizer.resetSyntax(); + tokenizer.wordChars(0,255); + tokenizer.whitespaceChars(0, ' '); + tokenizer.eolIsSignificant(true); + java.util.Vector vD = new java.util.Vector(); + + // Ignore initial empty lines + while (tokenizer.nextToken() == StreamTokenizer.TT_EOL); + if (tokenizer.ttype == StreamTokenizer.TT_EOF) + throw new java.io.IOException("Unexpected EOF on matrix read."); + do { + vD.addElement(Double.valueOf(tokenizer.sval)); // Read & store 1st row. + } while (tokenizer.nextToken() == StreamTokenizer.TT_WORD); + + int n = vD.size(); // Now we've got the number of columns! + double row[] = new double[n]; + for (int j=0; j v = new java.util.Vector(); + v.addElement(row); // Start storing rows instead of columns. + while (tokenizer.nextToken() == StreamTokenizer.TT_WORD) { + // While non-empty lines + v.addElement(row = new double[n]); + int j = 0; + do { + if (j >= n) throw new java.io.IOException + ("Row " + v.size() + " is too long."); + row[j++] = Double.valueOf(tokenizer.sval).doubleValue(); + } while (tokenizer.nextToken() == StreamTokenizer.TT_WORD); + if (j < n) throw new java.io.IOException + ("Row " + v.size() + " is too short."); + } + int m = v.size(); // Now we've got the number of rows. + double[][] A = new double[m][]; + v.copyInto(A); // copy the rows out of the vector + return new Matrix(A); + } + + +/* ------------------------ + Private Methods + * ------------------------ */ + + /** Check if size(A) == size(B) **/ + + private void checkMatrixDimensions (Matrix B) { + if (B.m != m || B.n != n) { + throw new IllegalArgumentException("Matrix dimensions must agree."); + } + } + + private static final long serialVersionUID = 1; +} diff --git a/Java/src/Jama/QRDecomposition.java b/Java/src/Jama/QRDecomposition.java new file mode 100644 index 00000000..10916be4 --- /dev/null +++ b/Java/src/Jama/QRDecomposition.java @@ -0,0 +1,219 @@ +package Jama; +import Jama.util.*; + +/** QR Decomposition. +

+ For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n + orthogonal matrix Q and an n-by-n upper triangular matrix R so that + A = Q*R. +

+ The QR decompostion always exists, even if the matrix does not have + full rank, so the constructor will never fail. The primary use of the + QR decomposition is in the least squares solution of nonsquare systems + of simultaneous linear equations. This will fail if isFullRank() + returns false. +*/ + +public class QRDecomposition implements java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Array for internal storage of decomposition. + @serial internal array storage. + */ + private double[][] QR; + + /** Row and column dimensions. + @serial column dimension. + @serial row dimension. + */ + private int m, n; + + /** Array for internal storage of diagonal of R. + @serial diagonal of R. + */ + private double[] Rdiag; + +/* ------------------------ + Constructor + * ------------------------ */ + + /** QR Decomposition, computed by Householder reflections. + Structure to access R and the Householder vectors and compute Q. + @param A Rectangular matrix + */ + + public QRDecomposition (Matrix A) { + // Initialize. + QR = A.getArrayCopy(); + m = A.getRowDimension(); + n = A.getColumnDimension(); + Rdiag = new double[n]; + + // Main loop. + for (int k = 0; k < n; k++) { + // Compute 2-norm of k-th column without under/overflow. + double nrm = 0; + for (int i = k; i < m; i++) { + nrm = Maths.hypot(nrm,QR[i][k]); + } + + if (nrm != 0.0) { + // Form k-th Householder vector. + if (QR[k][k] < 0) { + nrm = -nrm; + } + for (int i = k; i < m; i++) { + QR[i][k] /= nrm; + } + QR[k][k] += 1.0; + + // Apply transformation to remaining columns. + for (int j = k+1; j < n; j++) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k]*QR[i][j]; + } + s = -s/QR[k][k]; + for (int i = k; i < m; i++) { + QR[i][j] += s*QR[i][k]; + } + } + } + Rdiag[k] = -nrm; + } + } + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Is the matrix full rank? + @return true if R, and hence A, has full rank. + */ + + public boolean isFullRank () { + for (int j = 0; j < n; j++) { + if (Rdiag[j] == 0) + return false; + } + return true; + } + + /** Return the Householder vectors + @return Lower trapezoidal matrix whose columns define the reflections + */ + + public Matrix getH () { + Matrix X = new Matrix(m,n); + double[][] H = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i >= j) { + H[i][j] = QR[i][j]; + } else { + H[i][j] = 0.0; + } + } + } + return X; + } + + /** Return the upper triangular factor + @return R + */ + + public Matrix getR () { + Matrix X = new Matrix(n,n); + double[][] R = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i < j) { + R[i][j] = QR[i][j]; + } else if (i == j) { + R[i][j] = Rdiag[i]; + } else { + R[i][j] = 0.0; + } + } + } + return X; + } + + /** Generate and return the (economy-sized) orthogonal factor + @return Q + */ + + public Matrix getQ () { + Matrix X = new Matrix(m,n); + double[][] Q = X.getArray(); + for (int k = n-1; k >= 0; k--) { + for (int i = 0; i < m; i++) { + Q[i][k] = 0.0; + } + Q[k][k] = 1.0; + for (int j = k; j < n; j++) { + if (QR[k][k] != 0) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k]*Q[i][j]; + } + s = -s/QR[k][k]; + for (int i = k; i < m; i++) { + Q[i][j] += s*QR[i][k]; + } + } + } + } + return X; + } + + /** Least squares solution of A*X = B + @param B A Matrix with as many rows as A and any number of columns. + @return X that minimizes the two norm of Q*R*X-B. + @exception IllegalArgumentException Matrix row dimensions must agree. + @exception RuntimeException Matrix is rank deficient. + */ + + public Matrix solve (Matrix B) { + if (B.getRowDimension() != m) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!this.isFullRank()) { + throw new RuntimeException("Matrix is rank deficient."); + } + + // Copy right hand side + int nx = B.getColumnDimension(); + double[][] X = B.getArrayCopy(); + + // Compute Y = transpose(Q)*B + for (int k = 0; k < n; k++) { + for (int j = 0; j < nx; j++) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k]*X[i][j]; + } + s = -s/QR[k][k]; + for (int i = k; i < m; i++) { + X[i][j] += s*QR[i][k]; + } + } + } + // Solve R*X = Y; + for (int k = n-1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + X[k][j] /= Rdiag[k]; + } + for (int i = 0; i < k; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j]*QR[i][k]; + } + } + } + return (new Matrix(X,n,nx).getMatrix(0,n-1,0,nx-1)); + } + private static final long serialVersionUID = 1; +} diff --git a/Java/src/Jama/SingularValueDecomposition.java b/Java/src/Jama/SingularValueDecomposition.java new file mode 100644 index 00000000..b289e82f --- /dev/null +++ b/Java/src/Jama/SingularValueDecomposition.java @@ -0,0 +1,548 @@ +package Jama; +import Jama.util.*; + + /** Singular Value Decomposition. +

+ For an m-by-n matrix A with m >= n, the singular value decomposition is + an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and + an n-by-n orthogonal matrix V so that A = U*S*V'. +

+ The singular values, sigma[k] = S[k][k], are ordered so that + sigma[0] >= sigma[1] >= ... >= sigma[n-1]. +

+ The singular value decompostion always exists, so the constructor will + never fail. The matrix condition number and the effective numerical + rank can be computed from this decomposition. + */ + +public class SingularValueDecomposition implements java.io.Serializable { + +/* ------------------------ + Class variables + * ------------------------ */ + + /** Arrays for internal storage of U and V. + @serial internal storage of U. + @serial internal storage of V. + */ + private double[][] U, V; + + /** Array for internal storage of singular values. + @serial internal storage of singular values. + */ + private double[] s; + + /** Row and column dimensions. + @serial row dimension. + @serial column dimension. + */ + private int m, n; + +/* ------------------------ + Constructor + * ------------------------ */ + + /** Construct the singular value decomposition + Structure to access U, S and V. + @param Arg Rectangular matrix + */ + + public SingularValueDecomposition (Matrix Arg) { + + // Derived from LINPACK code. + // Initialize. + double[][] A = Arg.getArrayCopy(); + m = Arg.getRowDimension(); + n = Arg.getColumnDimension(); + + /* Apparently the failing cases are only a proper subset of (m= n"); } + */ + int nu = Math.min(m,n); + s = new double [Math.min(m+1,n)]; + U = new double [m][nu]; + V = new double [n][n]; + double[] e = new double [n]; + double[] work = new double [m]; + boolean wantu = true; + boolean wantv = true; + + // Reduce A to bidiagonal form, storing the diagonal elements + // in s and the super-diagonal elements in e. + + int nct = Math.min(m-1,n); + int nrt = Math.max(0,Math.min(n-2,m)); + for (int k = 0; k < Math.max(nct,nrt); k++) { + if (k < nct) { + + // Compute the transformation for the k-th column and + // place the k-th diagonal in s[k]. + // Compute 2-norm of k-th column without under/overflow. + s[k] = 0; + for (int i = k; i < m; i++) { + s[k] = Maths.hypot(s[k],A[i][k]); + } + if (s[k] != 0.0) { + if (A[k][k] < 0.0) { + s[k] = -s[k]; + } + for (int i = k; i < m; i++) { + A[i][k] /= s[k]; + } + A[k][k] += 1.0; + } + s[k] = -s[k]; + } + for (int j = k+1; j < n; j++) { + if ((k < nct) & (s[k] != 0.0)) { + + // Apply the transformation. + + double t = 0; + for (int i = k; i < m; i++) { + t += A[i][k]*A[i][j]; + } + t = -t/A[k][k]; + for (int i = k; i < m; i++) { + A[i][j] += t*A[i][k]; + } + } + + // Place the k-th row of A into e for the + // subsequent calculation of the row transformation. + + e[j] = A[k][j]; + } + if (wantu & (k < nct)) { + + // Place the transformation in U for subsequent back + // multiplication. + + for (int i = k; i < m; i++) { + U[i][k] = A[i][k]; + } + } + if (k < nrt) { + + // Compute the k-th row transformation and place the + // k-th super-diagonal in e[k]. + // Compute 2-norm without under/overflow. + e[k] = 0; + for (int i = k+1; i < n; i++) { + e[k] = Maths.hypot(e[k],e[i]); + } + if (e[k] != 0.0) { + if (e[k+1] < 0.0) { + e[k] = -e[k]; + } + for (int i = k+1; i < n; i++) { + e[i] /= e[k]; + } + e[k+1] += 1.0; + } + e[k] = -e[k]; + if ((k+1 < m) & (e[k] != 0.0)) { + + // Apply the transformation. + + for (int i = k+1; i < m; i++) { + work[i] = 0.0; + } + for (int j = k+1; j < n; j++) { + for (int i = k+1; i < m; i++) { + work[i] += e[j]*A[i][j]; + } + } + for (int j = k+1; j < n; j++) { + double t = -e[j]/e[k+1]; + for (int i = k+1; i < m; i++) { + A[i][j] += t*work[i]; + } + } + } + if (wantv) { + + // Place the transformation in V for subsequent + // back multiplication. + + for (int i = k+1; i < n; i++) { + V[i][k] = e[i]; + } + } + } + } + + // Set up the final bidiagonal matrix or order p. + + int p = Math.min(n,m+1); + if (nct < n) { + s[nct] = A[nct][nct]; + } + if (m < p) { + s[p-1] = 0.0; + } + if (nrt+1 < p) { + e[nrt] = A[nrt][p-1]; + } + e[p-1] = 0.0; + + // If required, generate U. + + if (wantu) { + for (int j = nct; j < nu; j++) { + for (int i = 0; i < m; i++) { + U[i][j] = 0.0; + } + U[j][j] = 1.0; + } + for (int k = nct-1; k >= 0; k--) { + if (s[k] != 0.0) { + for (int j = k+1; j < nu; j++) { + double t = 0; + for (int i = k; i < m; i++) { + t += U[i][k]*U[i][j]; + } + t = -t/U[k][k]; + for (int i = k; i < m; i++) { + U[i][j] += t*U[i][k]; + } + } + for (int i = k; i < m; i++ ) { + U[i][k] = -U[i][k]; + } + U[k][k] = 1.0 + U[k][k]; + for (int i = 0; i < k-1; i++) { + U[i][k] = 0.0; + } + } else { + for (int i = 0; i < m; i++) { + U[i][k] = 0.0; + } + U[k][k] = 1.0; + } + } + } + + // If required, generate V. + + if (wantv) { + for (int k = n-1; k >= 0; k--) { + if ((k < nrt) & (e[k] != 0.0)) { + for (int j = k+1; j < nu; j++) { + double t = 0; + for (int i = k+1; i < n; i++) { + t += V[i][k]*V[i][j]; + } + t = -t/V[k+1][k]; + for (int i = k+1; i < n; i++) { + V[i][j] += t*V[i][k]; + } + } + } + for (int i = 0; i < n; i++) { + V[i][k] = 0.0; + } + V[k][k] = 1.0; + } + } + + // Main iteration loop for the singular values. + + int pp = p-1; + int iter = 0; + double eps = Math.pow(2.0,-52.0); + double tiny = Math.pow(2.0,-966.0); + while (p > 0) { + int k,kase; + + // Here is where a test for too many iterations would go. + + // This section of the program inspects for + // negligible elements in the s and e arrays. On + // completion the variables kase and k are set as follows. + + // kase = 1 if s(p) and e[k-1] are negligible and k

= -1; k--) { + if (k == -1) { + break; + } + if (Math.abs(e[k]) <= + tiny + eps*(Math.abs(s[k]) + Math.abs(s[k+1]))) { + e[k] = 0.0; + break; + } + } + if (k == p-2) { + kase = 4; + } else { + int ks; + for (ks = p-1; ks >= k; ks--) { + if (ks == k) { + break; + } + double t = (ks != p ? Math.abs(e[ks]) : 0.) + + (ks != k+1 ? Math.abs(e[ks-1]) : 0.); + if (Math.abs(s[ks]) <= tiny + eps*t) { + s[ks] = 0.0; + break; + } + } + if (ks == k) { + kase = 3; + } else if (ks == p-1) { + kase = 1; + } else { + kase = 2; + k = ks; + } + } + k++; + + // Perform the task indicated by kase. + + switch (kase) { + + // Deflate negligible s(p). + + case 1: { + double f = e[p-2]; + e[p-2] = 0.0; + for (int j = p-2; j >= k; j--) { + double t = Maths.hypot(s[j],f); + double cs = s[j]/t; + double sn = f/t; + s[j] = t; + if (j != k) { + f = -sn*e[j-1]; + e[j-1] = cs*e[j-1]; + } + if (wantv) { + for (int i = 0; i < n; i++) { + t = cs*V[i][j] + sn*V[i][p-1]; + V[i][p-1] = -sn*V[i][j] + cs*V[i][p-1]; + V[i][j] = t; + } + } + } + } + break; + + // Split at negligible s(k). + + case 2: { + double f = e[k-1]; + e[k-1] = 0.0; + for (int j = k; j < p; j++) { + double t = Maths.hypot(s[j],f); + double cs = s[j]/t; + double sn = f/t; + s[j] = t; + f = -sn*e[j]; + e[j] = cs*e[j]; + if (wantu) { + for (int i = 0; i < m; i++) { + t = cs*U[i][j] + sn*U[i][k-1]; + U[i][k-1] = -sn*U[i][j] + cs*U[i][k-1]; + U[i][j] = t; + } + } + } + } + break; + + // Perform one qr step. + + case 3: { + + // Calculate the shift. + + double scale = Math.max(Math.max(Math.max(Math.max( + Math.abs(s[p-1]),Math.abs(s[p-2])),Math.abs(e[p-2])), + Math.abs(s[k])),Math.abs(e[k])); + double sp = s[p-1]/scale; + double spm1 = s[p-2]/scale; + double epm1 = e[p-2]/scale; + double sk = s[k]/scale; + double ek = e[k]/scale; + double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0; + double c = (sp*epm1)*(sp*epm1); + double shift = 0.0; + if ((b != 0.0) | (c != 0.0)) { + shift = Math.sqrt(b*b + c); + if (b < 0.0) { + shift = -shift; + } + shift = c/(b + shift); + } + double f = (sk + sp)*(sk - sp) + shift; + double g = sk*ek; + + // Chase zeros. + + for (int j = k; j < p-1; j++) { + double t = Maths.hypot(f,g); + double cs = f/t; + double sn = g/t; + if (j != k) { + e[j-1] = t; + } + f = cs*s[j] + sn*e[j]; + e[j] = cs*e[j] - sn*s[j]; + g = sn*s[j+1]; + s[j+1] = cs*s[j+1]; + if (wantv) { + for (int i = 0; i < n; i++) { + t = cs*V[i][j] + sn*V[i][j+1]; + V[i][j+1] = -sn*V[i][j] + cs*V[i][j+1]; + V[i][j] = t; + } + } + t = Maths.hypot(f,g); + cs = f/t; + sn = g/t; + s[j] = t; + f = cs*e[j] + sn*s[j+1]; + s[j+1] = -sn*e[j] + cs*s[j+1]; + g = sn*e[j+1]; + e[j+1] = cs*e[j+1]; + if (wantu && (j < m-1)) { + for (int i = 0; i < m; i++) { + t = cs*U[i][j] + sn*U[i][j+1]; + U[i][j+1] = -sn*U[i][j] + cs*U[i][j+1]; + U[i][j] = t; + } + } + } + e[p-2] = f; + iter = iter + 1; + } + break; + + // Convergence. + + case 4: { + + // Make the singular values positive. + + if (s[k] <= 0.0) { + s[k] = (s[k] < 0.0 ? -s[k] : 0.0); + if (wantv) { + for (int i = 0; i <= pp; i++) { + V[i][k] = -V[i][k]; + } + } + } + + // Order the singular values. + + while (k < pp) { + if (s[k] >= s[k+1]) { + break; + } + double t = s[k]; + s[k] = s[k+1]; + s[k+1] = t; + if (wantv && (k < n-1)) { + for (int i = 0; i < n; i++) { + t = V[i][k+1]; V[i][k+1] = V[i][k]; V[i][k] = t; + } + } + if (wantu && (k < m-1)) { + for (int i = 0; i < m; i++) { + t = U[i][k+1]; U[i][k+1] = U[i][k]; U[i][k] = t; + } + } + k++; + } + iter = 0; + p--; + } + break; + } + } + } + +/* ------------------------ + Public Methods + * ------------------------ */ + + /** Return the left singular vectors + @return U + */ + + public Matrix getU () { + return new Matrix(U,m,Math.min(m+1,n)); + } + + /** Return the right singular vectors + @return V + */ + + public Matrix getV () { + return new Matrix(V,n,n); + } + + /** Return the one-dimensional array of singular values + @return diagonal of S. + */ + + public double[] getSingularValues () { + return s; + } + + /** Return the diagonal matrix of singular values + @return S + */ + + public Matrix getS () { + Matrix X = new Matrix(n,n); + double[][] S = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + S[i][j] = 0.0; + } + S[i][i] = this.s[i]; + } + return X; + } + + /** Two norm + @return max(S) + */ + + public double norm2 () { + return s[0]; + } + + /** Two norm condition number + @return max(S)/min(S) + */ + + public double cond () { + return s[0]/s[Math.min(m,n)-1]; + } + + /** Effective numerical matrix rank + @return Number of nonnegligible singular values. + */ + + public int rank () { + double eps = Math.pow(2.0,-52.0); + double tol = Math.max(m,n)*s[0]*eps; + int r = 0; + for (int i = 0; i < s.length; i++) { + if (s[i] > tol) { + r++; + } + } + return r; + } + private static final long serialVersionUID = 1; +} diff --git a/Java/src/Jama/doc/Jama/CholeskyDecomposition.html b/Java/src/Jama/doc/Jama/CholeskyDecomposition.html new file mode 100644 index 00000000..4f1721c2 --- /dev/null +++ b/Java/src/Jama/doc/Jama/CholeskyDecomposition.html @@ -0,0 +1,328 @@ + + + + + + +CholeskyDecomposition + + + + + + + + + + + + +


+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class CholeskyDecomposition

+
+java.lang.Object
+  extended by Jama.CholeskyDecomposition
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class CholeskyDecomposition
extends Object
implements Serializable
+ + +

+Cholesky Decomposition. +

+ For a symmetric, positive definite matrix A, the Cholesky decomposition + is an lower triangular matrix L so that A = L*L'. +

+ If the matrix is not symmetric or positive definite, the constructor + returns a partial decomposition and sets an internal flag that may + be queried by the isSPD() method. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
CholeskyDecomposition(Matrix Arg) + +
+          Cholesky algorithm for symmetric and positive definite matrix.
+  + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ MatrixgetL() + +
+          Return triangular factor.
+ booleanisSPD() + +
+          Is the matrix symmetric and positive definite?
+ Matrixsolve(Matrix B) + +
+          Solve A*X = B
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+CholeskyDecomposition

+
+public CholeskyDecomposition(Matrix Arg)
+
+
Cholesky algorithm for symmetric and positive definite matrix. + Structure to access L and isspd flag. +

+

+
Parameters:
Arg - Square, symmetric matrix.
+
+ + + + + + + + +
+Method Detail
+ +

+isSPD

+
+public boolean isSPD()
+
+
Is the matrix symmetric and positive definite? +

+

+
+
+
+ +
Returns:
true if A is symmetric and positive definite.
+
+
+
+ +

+getL

+
+public Matrix getL()
+
+
Return triangular factor. +

+

+
+
+
+ +
Returns:
L
+
+
+
+ +

+solve

+
+public Matrix solve(Matrix B)
+
+
Solve A*X = B +

+

+
+
+
+
Parameters:
B - A Matrix with as many rows as A and any number of columns. +
Returns:
X so that L*L'*X = B +
Throws: +
IllegalArgumentException - Matrix row dimensions must agree. +
RuntimeException - Matrix is not symmetric positive definite.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/EigenvalueDecomposition.html b/Java/src/Jama/doc/Jama/EigenvalueDecomposition.html new file mode 100644 index 00000000..a27c2b5c --- /dev/null +++ b/Java/src/Jama/doc/Jama/EigenvalueDecomposition.html @@ -0,0 +1,357 @@ + + + + + + +EigenvalueDecomposition + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class EigenvalueDecomposition

+
+java.lang.Object
+  extended by Jama.EigenvalueDecomposition
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class EigenvalueDecomposition
extends Object
implements Serializable
+ + +

+Eigenvalues and eigenvectors of a real matrix. +

+ If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is + diagonal and the eigenvector matrix V is orthogonal. + I.e. A = V.times(D.times(V.transpose())) and + V.times(V.transpose()) equals the identity matrix. +

+ If A is not symmetric, then the eigenvalue matrix D is block diagonal + with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, + lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The + columns of V represent the eigenvectors in the sense that A*V = V*D, + i.e. A.times(V) equals V.times(D). The matrix V may be badly + conditioned, or even singular, so the validity of the equation + A = V*D*inverse(V) depends upon V.cond(). +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
EigenvalueDecomposition(Matrix Arg) + +
+          Check for symmetry, then construct the eigenvalue decomposition + Structure to access D and V.
+  + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ MatrixgetD() + +
+          Return the block diagonal eigenvalue matrix
+ double[]getImagEigenvalues() + +
+          Return the imaginary parts of the eigenvalues
+ double[]getRealEigenvalues() + +
+          Return the real parts of the eigenvalues
+ MatrixgetV() + +
+          Return the eigenvector matrix
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+EigenvalueDecomposition

+
+public EigenvalueDecomposition(Matrix Arg)
+
+
Check for symmetry, then construct the eigenvalue decomposition + Structure to access D and V. +

+

+
Parameters:
Arg - Square matrix
+
+ + + + + + + + +
+Method Detail
+ +

+getV

+
+public Matrix getV()
+
+
Return the eigenvector matrix +

+

+
+
+
+ +
Returns:
V
+
+
+
+ +

+getRealEigenvalues

+
+public double[] getRealEigenvalues()
+
+
Return the real parts of the eigenvalues +

+

+
+
+
+ +
Returns:
real(diag(D))
+
+
+
+ +

+getImagEigenvalues

+
+public double[] getImagEigenvalues()
+
+
Return the imaginary parts of the eigenvalues +

+

+
+
+
+ +
Returns:
imag(diag(D))
+
+
+
+ +

+getD

+
+public Matrix getD()
+
+
Return the block diagonal eigenvalue matrix +

+

+
+
+
+ +
Returns:
D
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/LUDecomposition.html b/Java/src/Jama/doc/Jama/LUDecomposition.html new file mode 100644 index 00000000..375d0b61 --- /dev/null +++ b/Java/src/Jama/doc/Jama/LUDecomposition.html @@ -0,0 +1,434 @@ + + + + + + +LUDecomposition + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class LUDecomposition

+
+java.lang.Object
+  extended by Jama.LUDecomposition
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class LUDecomposition
extends Object
implements Serializable
+ + +

+LU Decomposition. +

+ For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n + unit lower triangular matrix L, an n-by-n upper triangular matrix U, + and a permutation vector piv of length m so that A(piv,:) = L*U. + If m < n, then L is m-by-m and U is m-by-n. +

+ The LU decompostion with pivoting always exists, even if the matrix is + singular, so the constructor will never fail. The primary use of the + LU decomposition is in the solution of square systems of simultaneous + linear equations. This will fail if isNonsingular() returns false. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
LUDecomposition(Matrix A) + +
+          LU Decomposition + Structure to access L, U and piv.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ doubledet() + +
+          Determinant
+ double[]getDoublePivot() + +
+          Return pivot permutation vector as a one-dimensional double array
+ MatrixgetL() + +
+          Return lower triangular factor
+ int[]getPivot() + +
+          Return pivot permutation vector
+ MatrixgetU() + +
+          Return upper triangular factor
+ booleanisNonsingular() + +
+          Is the matrix nonsingular?
+ Matrixsolve(Matrix B) + +
+          Solve A*X = B
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+LUDecomposition

+
+public LUDecomposition(Matrix A)
+
+
LU Decomposition + Structure to access L, U and piv. +

+

+
Parameters:
A - Rectangular matrix
+
+ + + + + + + + +
+Method Detail
+ +

+isNonsingular

+
+public boolean isNonsingular()
+
+
Is the matrix nonsingular? +

+

+
+
+
+ +
Returns:
true if U, and hence A, is nonsingular.
+
+
+
+ +

+getL

+
+public Matrix getL()
+
+
Return lower triangular factor +

+

+
+
+
+ +
Returns:
L
+
+
+
+ +

+getU

+
+public Matrix getU()
+
+
Return upper triangular factor +

+

+
+
+
+ +
Returns:
U
+
+
+
+ +

+getPivot

+
+public int[] getPivot()
+
+
Return pivot permutation vector +

+

+
+
+
+ +
Returns:
piv
+
+
+
+ +

+getDoublePivot

+
+public double[] getDoublePivot()
+
+
Return pivot permutation vector as a one-dimensional double array +

+

+
+
+
+ +
Returns:
(double) piv
+
+
+
+ +

+det

+
+public double det()
+
+
Determinant +

+

+
+
+
+ +
Returns:
det(A) +
Throws: +
IllegalArgumentException - Matrix must be square
+
+
+
+ +

+solve

+
+public Matrix solve(Matrix B)
+
+
Solve A*X = B +

+

+
+
+
+
Parameters:
B - A Matrix with as many rows as A and any number of columns. +
Returns:
X so that L*U*X = B(piv,:) +
Throws: +
IllegalArgumentException - Matrix row dimensions must agree. +
RuntimeException - Matrix is singular.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/Matrix.html b/Java/src/Jama/doc/Jama/Matrix.html new file mode 100644 index 00000000..a660a61a --- /dev/null +++ b/Java/src/Jama/doc/Jama/Matrix.html @@ -0,0 +1,1881 @@ + + + + + + +Matrix + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class Matrix

+
+java.lang.Object
+  extended by Jama.Matrix
+
+
+
All Implemented Interfaces:
Serializable, Cloneable
+
+
+
+
public class Matrix
extends Object
implements Cloneable, Serializable
+ + +

+Jama = Java Matrix class. +

+ The Java Matrix Class provides the fundamental operations of numerical + linear algebra. Various constructors create Matrices from two dimensional + arrays of double precision floating point numbers. Various "gets" and + "sets" provide access to submatrices and matrix elements. Several methods + implement basic matrix arithmetic, including matrix addition and + multiplication, matrix norms, and element-by-element array operations. + Methods for reading and printing matrices are also included. All the + operations in this version of the Matrix Class involve real matrices. + Complex matrices may be handled in a future version. +

+ Five fundamental matrix decompositions, which consist of pairs or triples + of matrices, permutation vectors, and the like, produce results in five + decomposition classes. These decompositions are accessed by the Matrix + class to compute solutions of simultaneous linear equations, determinants, + inverses and other matrix functions. The five decompositions are: +

    +
  • Cholesky Decomposition of symmetric, positive definite matrices. +
  • LU Decomposition of rectangular matrices. +
  • QR Decomposition of rectangular matrices. +
  • Singular Value Decomposition of rectangular matrices. +
  • Eigenvalue Decomposition of both symmetric and nonsymmetric square matrices. +
+
+
Example of use:
+

+

Solve a linear system A x = b and compute the residual norm, ||b - A x||. +

+      double[][] vals = {{1.,2.,3},{4.,5.,6.},{7.,8.,10.}};
+      Matrix A = new Matrix(vals);
+      Matrix b = Matrix.random(3,1);
+      Matrix x = A.solve(b);
+      Matrix r = A.times(x).minus(b);
+      double rnorm = r.normInf();
+
+
+

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + + + + + + + + + + + + + +
+Constructor Summary
Matrix(double[][] A) + +
+          Construct a matrix from a 2-D array.
Matrix(double[][] A, + int m, + int n) + +
+          Construct a matrix quickly without checking arguments.
Matrix(double[] vals, + int m) + +
+          Construct a matrix from a one-dimensional packed array
Matrix(int m, + int n) + +
+          Construct an m-by-n matrix of zeros.
Matrix(int m, + int n, + double s) + +
+          Construct an m-by-n constant matrix.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ MatrixarrayLeftDivide(Matrix B) + +
+          Element-by-element left division, C = A.\B
+ MatrixarrayLeftDivideEquals(Matrix B) + +
+          Element-by-element left division in place, A = A.\B
+ MatrixarrayRightDivide(Matrix B) + +
+          Element-by-element right division, C = A./B
+ MatrixarrayRightDivideEquals(Matrix B) + +
+          Element-by-element right division in place, A = A./B
+ MatrixarrayTimes(Matrix B) + +
+          Element-by-element multiplication, C = A.*B
+ MatrixarrayTimesEquals(Matrix B) + +
+          Element-by-element multiplication in place, A = A.*B
+ CholeskyDecompositionchol() + +
+          Cholesky Decomposition
+ Objectclone() + +
+          Clone the Matrix object.
+ doublecond() + +
+          Matrix condition (2 norm)
+static MatrixconstructWithCopy(double[][] A) + +
+          Construct a matrix from a copy of a 2-D array.
+ Matrixcopy() + +
+          Make a deep copy of a matrix
+ doubledet() + +
+          Matrix determinant
+ EigenvalueDecompositioneig() + +
+          Eigenvalue Decomposition
+ doubleget(int i, + int j) + +
+          Get a single element.
+ double[][]getArray() + +
+          Access the internal two-dimensional array.
+ double[][]getArrayCopy() + +
+          Copy the internal two-dimensional array.
+ intgetColumnDimension() + +
+          Get column dimension.
+ double[]getColumnPackedCopy() + +
+          Make a one-dimensional column packed copy of the internal array.
+ MatrixgetMatrix(int[] r, + int[] c) + +
+          Get a submatrix.
+ MatrixgetMatrix(int[] r, + int j0, + int j1) + +
+          Get a submatrix.
+ MatrixgetMatrix(int i0, + int i1, + int[] c) + +
+          Get a submatrix.
+ MatrixgetMatrix(int i0, + int i1, + int j0, + int j1) + +
+          Get a submatrix.
+ intgetRowDimension() + +
+          Get row dimension.
+ double[]getRowPackedCopy() + +
+          Make a one-dimensional row packed copy of the internal array.
+static Matrixidentity(int m, + int n) + +
+          Generate identity matrix
+ Matrixinverse() + +
+          Matrix inverse or pseudoinverse
+ LUDecompositionlu() + +
+          LU Decomposition
+ Matrixminus(Matrix B) + +
+          C = A - B
+ MatrixminusEquals(Matrix B) + +
+          A = A - B
+ doublenorm1() + +
+          One norm
+ doublenorm2() + +
+          Two norm
+ doublenormF() + +
+          Frobenius norm
+ doublenormInf() + +
+          Infinity norm
+ Matrixplus(Matrix B) + +
+          C = A + B
+ MatrixplusEquals(Matrix B) + +
+          A = A + B
+ voidprint(int w, + int d) + +
+          Print the matrix to stdout.
+ voidprint(NumberFormat format, + int width) + +
+          Print the matrix to stdout.
+ voidprint(PrintWriter output, + int w, + int d) + +
+          Print the matrix to the output stream.
+ voidprint(PrintWriter output, + NumberFormat format, + int width) + +
+          Print the matrix to the output stream.
+ QRDecompositionqr() + +
+          QR Decomposition
+static Matrixrandom(int m, + int n) + +
+          Generate matrix with random elements
+ intrank() + +
+          Matrix rank
+static Matrixread(BufferedReader input) + +
+          Read a matrix from a stream.
+ voidset(int i, + int j, + double s) + +
+          Set a single element.
+ voidsetMatrix(int[] r, + int[] c, + Matrix X) + +
+          Set a submatrix.
+ voidsetMatrix(int[] r, + int j0, + int j1, + Matrix X) + +
+          Set a submatrix.
+ voidsetMatrix(int i0, + int i1, + int[] c, + Matrix X) + +
+          Set a submatrix.
+ voidsetMatrix(int i0, + int i1, + int j0, + int j1, + Matrix X) + +
+          Set a submatrix.
+ Matrixsolve(Matrix B) + +
+          Solve A*X = B
+ MatrixsolveTranspose(Matrix B) + +
+          Solve X*A = B, which is also A'*X' = B'
+ SingularValueDecompositionsvd() + +
+          Singular Value Decomposition
+ Matrixtimes(double s) + +
+          Multiply a matrix by a scalar, C = s*A
+ Matrixtimes(Matrix B) + +
+          Linear algebraic matrix multiplication, A * B
+ MatrixtimesEquals(double s) + +
+          Multiply a matrix by a scalar in place, A = s*A
+ doubletrace() + +
+          Matrix trace.
+ Matrixtranspose() + +
+          Matrix transpose.
+ Matrixuminus() + +
+          Unary minus
+ + + + + + + +
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+Matrix

+
+public Matrix(int m,
+              int n)
+
+
Construct an m-by-n matrix of zeros. +

+

+
Parameters:
m - Number of rows.
n - Number of colums.
+
+
+ +

+Matrix

+
+public Matrix(int m,
+              int n,
+              double s)
+
+
Construct an m-by-n constant matrix. +

+

+
Parameters:
m - Number of rows.
n - Number of colums.
s - Fill the matrix with this scalar value.
+
+
+ +

+Matrix

+
+public Matrix(double[][] A)
+
+
Construct a matrix from a 2-D array. +

+

+
Parameters:
A - Two-dimensional array of doubles. +
Throws: +
IllegalArgumentException - All rows must have the same length
See Also:
constructWithCopy(double[][])
+
+
+ +

+Matrix

+
+public Matrix(double[][] A,
+              int m,
+              int n)
+
+
Construct a matrix quickly without checking arguments. +

+

+
Parameters:
A - Two-dimensional array of doubles.
m - Number of rows.
n - Number of colums.
+
+
+ +

+Matrix

+
+public Matrix(double[] vals,
+              int m)
+
+
Construct a matrix from a one-dimensional packed array +

+

+
Parameters:
vals - One-dimensional array of doubles, packed by columns (ala Fortran).
m - Number of rows. +
Throws: +
IllegalArgumentException - Array length must be a multiple of m.
+
+ + + + + + + + +
+Method Detail
+ +

+constructWithCopy

+
+public static Matrix constructWithCopy(double[][] A)
+
+
Construct a matrix from a copy of a 2-D array. +

+

+
+
+
+
Parameters:
A - Two-dimensional array of doubles. +
Throws: +
IllegalArgumentException - All rows must have the same length
+
+
+
+ +

+copy

+
+public Matrix copy()
+
+
Make a deep copy of a matrix +

+

+
+
+
+
+
+
+
+ +

+clone

+
+public Object clone()
+
+
Clone the Matrix object. +

+

+
Overrides:
clone in class Object
+
+
+
+
+
+
+ +

+getArray

+
+public double[][] getArray()
+
+
Access the internal two-dimensional array. +

+

+
+
+
+ +
Returns:
Pointer to the two-dimensional array of matrix elements.
+
+
+
+ +

+getArrayCopy

+
+public double[][] getArrayCopy()
+
+
Copy the internal two-dimensional array. +

+

+
+
+
+ +
Returns:
Two-dimensional array copy of matrix elements.
+
+
+
+ +

+getColumnPackedCopy

+
+public double[] getColumnPackedCopy()
+
+
Make a one-dimensional column packed copy of the internal array. +

+

+
+
+
+ +
Returns:
Matrix elements packed in a one-dimensional array by columns.
+
+
+
+ +

+getRowPackedCopy

+
+public double[] getRowPackedCopy()
+
+
Make a one-dimensional row packed copy of the internal array. +

+

+
+
+
+ +
Returns:
Matrix elements packed in a one-dimensional array by rows.
+
+
+
+ +

+getRowDimension

+
+public int getRowDimension()
+
+
Get row dimension. +

+

+
+
+
+ +
Returns:
m, the number of rows.
+
+
+
+ +

+getColumnDimension

+
+public int getColumnDimension()
+
+
Get column dimension. +

+

+
+
+
+ +
Returns:
n, the number of columns.
+
+
+
+ +

+get

+
+public double get(int i,
+                  int j)
+
+
Get a single element. +

+

+
+
+
+
Parameters:
i - Row index.
j - Column index. +
Returns:
A(i,j) +
Throws: +
ArrayIndexOutOfBoundsException
+
+
+
+ +

+getMatrix

+
+public Matrix getMatrix(int i0,
+                        int i1,
+                        int j0,
+                        int j1)
+
+
Get a submatrix. +

+

+
+
+
+
Parameters:
i0 - Initial row index
i1 - Final row index
j0 - Initial column index
j1 - Final column index +
Returns:
A(i0:i1,j0:j1) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+getMatrix

+
+public Matrix getMatrix(int[] r,
+                        int[] c)
+
+
Get a submatrix. +

+

+
+
+
+
Parameters:
r - Array of row indices.
c - Array of column indices. +
Returns:
A(r(:),c(:)) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+getMatrix

+
+public Matrix getMatrix(int i0,
+                        int i1,
+                        int[] c)
+
+
Get a submatrix. +

+

+
+
+
+
Parameters:
i0 - Initial row index
i1 - Final row index
c - Array of column indices. +
Returns:
A(i0:i1,c(:)) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+getMatrix

+
+public Matrix getMatrix(int[] r,
+                        int j0,
+                        int j1)
+
+
Get a submatrix. +

+

+
+
+
+
Parameters:
r - Array of row indices.
j0 - Initial column index
j1 - Final column index +
Returns:
A(r(:),j0:j1) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+set

+
+public void set(int i,
+                int j,
+                double s)
+
+
Set a single element. +

+

+
+
+
+
Parameters:
i - Row index.
j - Column index.
s - A(i,j). +
Throws: +
ArrayIndexOutOfBoundsException
+
+
+
+ +

+setMatrix

+
+public void setMatrix(int i0,
+                      int i1,
+                      int j0,
+                      int j1,
+                      Matrix X)
+
+
Set a submatrix. +

+

+
+
+
+
Parameters:
i0 - Initial row index
i1 - Final row index
j0 - Initial column index
j1 - Final column index
X - A(i0:i1,j0:j1) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+setMatrix

+
+public void setMatrix(int[] r,
+                      int[] c,
+                      Matrix X)
+
+
Set a submatrix. +

+

+
+
+
+
Parameters:
r - Array of row indices.
c - Array of column indices.
X - A(r(:),c(:)) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+setMatrix

+
+public void setMatrix(int[] r,
+                      int j0,
+                      int j1,
+                      Matrix X)
+
+
Set a submatrix. +

+

+
+
+
+
Parameters:
r - Array of row indices.
j0 - Initial column index
j1 - Final column index
X - A(r(:),j0:j1) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+setMatrix

+
+public void setMatrix(int i0,
+                      int i1,
+                      int[] c,
+                      Matrix X)
+
+
Set a submatrix. +

+

+
+
+
+
Parameters:
i0 - Initial row index
i1 - Final row index
c - Array of column indices.
X - A(i0:i1,c(:)) +
Throws: +
ArrayIndexOutOfBoundsException - Submatrix indices
+
+
+
+ +

+transpose

+
+public Matrix transpose()
+
+
Matrix transpose. +

+

+
+
+
+ +
Returns:
A'
+
+
+
+ +

+norm1

+
+public double norm1()
+
+
One norm +

+

+
+
+
+ +
Returns:
maximum column sum.
+
+
+
+ +

+norm2

+
+public double norm2()
+
+
Two norm +

+

+
+
+
+ +
Returns:
maximum singular value.
+
+
+
+ +

+normInf

+
+public double normInf()
+
+
Infinity norm +

+

+
+
+
+ +
Returns:
maximum row sum.
+
+
+
+ +

+normF

+
+public double normF()
+
+
Frobenius norm +

+

+
+
+
+ +
Returns:
sqrt of sum of squares of all elements.
+
+
+
+ +

+uminus

+
+public Matrix uminus()
+
+
Unary minus +

+

+
+
+
+ +
Returns:
-A
+
+
+
+ +

+plus

+
+public Matrix plus(Matrix B)
+
+
C = A + B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A + B
+
+
+
+ +

+plusEquals

+
+public Matrix plusEquals(Matrix B)
+
+
A = A + B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A + B
+
+
+
+ +

+minus

+
+public Matrix minus(Matrix B)
+
+
C = A - B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A - B
+
+
+
+ +

+minusEquals

+
+public Matrix minusEquals(Matrix B)
+
+
A = A - B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A - B
+
+
+
+ +

+arrayTimes

+
+public Matrix arrayTimes(Matrix B)
+
+
Element-by-element multiplication, C = A.*B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A.*B
+
+
+
+ +

+arrayTimesEquals

+
+public Matrix arrayTimesEquals(Matrix B)
+
+
Element-by-element multiplication in place, A = A.*B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A.*B
+
+
+
+ +

+arrayRightDivide

+
+public Matrix arrayRightDivide(Matrix B)
+
+
Element-by-element right division, C = A./B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A./B
+
+
+
+ +

+arrayRightDivideEquals

+
+public Matrix arrayRightDivideEquals(Matrix B)
+
+
Element-by-element right division in place, A = A./B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A./B
+
+
+
+ +

+arrayLeftDivide

+
+public Matrix arrayLeftDivide(Matrix B)
+
+
Element-by-element left division, C = A.\B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A.\B
+
+
+
+ +

+arrayLeftDivideEquals

+
+public Matrix arrayLeftDivideEquals(Matrix B)
+
+
Element-by-element left division in place, A = A.\B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
A.\B
+
+
+
+ +

+times

+
+public Matrix times(double s)
+
+
Multiply a matrix by a scalar, C = s*A +

+

+
+
+
+
Parameters:
s - scalar +
Returns:
s*A
+
+
+
+ +

+timesEquals

+
+public Matrix timesEquals(double s)
+
+
Multiply a matrix by a scalar in place, A = s*A +

+

+
+
+
+
Parameters:
s - scalar +
Returns:
replace A by s*A
+
+
+
+ +

+times

+
+public Matrix times(Matrix B)
+
+
Linear algebraic matrix multiplication, A * B +

+

+
+
+
+
Parameters:
B - another matrix +
Returns:
Matrix product, A * B +
Throws: +
IllegalArgumentException - Matrix inner dimensions must agree.
+
+
+
+ +

+lu

+
+public LUDecomposition lu()
+
+
LU Decomposition +

+

+
+
+
+ +
Returns:
LUDecomposition
See Also:
LUDecomposition
+
+
+
+ +

+qr

+
+public QRDecomposition qr()
+
+
QR Decomposition +

+

+
+
+
+ +
Returns:
QRDecomposition
See Also:
QRDecomposition
+
+
+
+ +

+chol

+
+public CholeskyDecomposition chol()
+
+
Cholesky Decomposition +

+

+
+
+
+ +
Returns:
CholeskyDecomposition
See Also:
CholeskyDecomposition
+
+
+
+ +

+svd

+
+public SingularValueDecomposition svd()
+
+
Singular Value Decomposition +

+

+
+
+
+ +
Returns:
SingularValueDecomposition
See Also:
SingularValueDecomposition
+
+
+
+ +

+eig

+
+public EigenvalueDecomposition eig()
+
+
Eigenvalue Decomposition +

+

+
+
+
+ +
Returns:
EigenvalueDecomposition
See Also:
EigenvalueDecomposition
+
+
+
+ +

+solve

+
+public Matrix solve(Matrix B)
+
+
Solve A*X = B +

+

+
+
+
+
Parameters:
B - right hand side +
Returns:
solution if A is square, least squares solution otherwise
+
+
+
+ +

+solveTranspose

+
+public Matrix solveTranspose(Matrix B)
+
+
Solve X*A = B, which is also A'*X' = B' +

+

+
+
+
+
Parameters:
B - right hand side +
Returns:
solution if A is square, least squares solution otherwise.
+
+
+
+ +

+inverse

+
+public Matrix inverse()
+
+
Matrix inverse or pseudoinverse +

+

+
+
+
+ +
Returns:
inverse(A) if A is square, pseudoinverse otherwise.
+
+
+
+ +

+det

+
+public double det()
+
+
Matrix determinant +

+

+
+
+
+ +
Returns:
determinant
+
+
+
+ +

+rank

+
+public int rank()
+
+
Matrix rank +

+

+
+
+
+ +
Returns:
effective numerical rank, obtained from SVD.
+
+
+
+ +

+cond

+
+public double cond()
+
+
Matrix condition (2 norm) +

+

+
+
+
+ +
Returns:
ratio of largest to smallest singular value.
+
+
+
+ +

+trace

+
+public double trace()
+
+
Matrix trace. +

+

+
+
+
+ +
Returns:
sum of the diagonal elements.
+
+
+
+ +

+random

+
+public static Matrix random(int m,
+                            int n)
+
+
Generate matrix with random elements +

+

+
+
+
+
Parameters:
m - Number of rows.
n - Number of colums. +
Returns:
An m-by-n matrix with uniformly distributed random elements.
+
+
+
+ +

+identity

+
+public static Matrix identity(int m,
+                              int n)
+
+
Generate identity matrix +

+

+
+
+
+
Parameters:
m - Number of rows.
n - Number of colums. +
Returns:
An m-by-n matrix with ones on the diagonal and zeros elsewhere.
+
+
+
+ +

+print

+
+public void print(int w,
+                  int d)
+
+
Print the matrix to stdout. Line the elements up in columns + with a Fortran-like 'Fw.d' style format. +

+

+
+
+
+
Parameters:
w - Column width.
d - Number of digits after the decimal.
+
+
+
+ +

+print

+
+public void print(PrintWriter output,
+                  int w,
+                  int d)
+
+
Print the matrix to the output stream. Line the elements up in + columns with a Fortran-like 'Fw.d' style format. +

+

+
+
+
+
Parameters:
output - Output stream.
w - Column width.
d - Number of digits after the decimal.
+
+
+
+ +

+print

+
+public void print(NumberFormat format,
+                  int width)
+
+
Print the matrix to stdout. Line the elements up in columns. + Use the format object, and right justify within columns of width + characters. + Note that is the matrix is to be read back in, you probably will want + to use a NumberFormat that is set to US Locale. +

+

+
+
+
+
Parameters:
format - A Formatting object for individual elements.
width - Field width for each column.
See Also:
DecimalFormat.setDecimalFormatSymbols(java.text.DecimalFormatSymbols)
+
+
+
+ +

+print

+
+public void print(PrintWriter output,
+                  NumberFormat format,
+                  int width)
+
+
Print the matrix to the output stream. Line the elements up in columns. + Use the format object, and right justify within columns of width + characters. + Note that is the matrix is to be read back in, you probably will want + to use a NumberFormat that is set to US Locale. +

+

+
+
+
+
Parameters:
output - the output stream.
format - A formatting object to format the matrix elements
width - Column width.
See Also:
DecimalFormat.setDecimalFormatSymbols(java.text.DecimalFormatSymbols)
+
+
+
+ +

+read

+
+public static Matrix read(BufferedReader input)
+                   throws IOException
+
+
Read a matrix from a stream. The format is the same the print method, + so printed matrices can be read back in (provided they were printed using + US Locale). Elements are separated by + whitespace, all the elements for each row appear on a single line, + the last row is followed by a blank line. +

+

+
+
+
+
Parameters:
input - the input stream. +
Throws: +
IOException
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/QRDecomposition.html b/Java/src/Jama/doc/Jama/QRDecomposition.html new file mode 100644 index 00000000..46a760ef --- /dev/null +++ b/Java/src/Jama/doc/Jama/QRDecomposition.html @@ -0,0 +1,381 @@ + + + + + + +QRDecomposition + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class QRDecomposition

+
+java.lang.Object
+  extended by Jama.QRDecomposition
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class QRDecomposition
extends Object
implements Serializable
+ + +

+QR Decomposition. +

+ For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n + orthogonal matrix Q and an n-by-n upper triangular matrix R so that + A = Q*R. +

+ The QR decompostion always exists, even if the matrix does not have + full rank, so the constructor will never fail. The primary use of the + QR decomposition is in the least squares solution of nonsquare systems + of simultaneous linear equations. This will fail if isFullRank() + returns false. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
QRDecomposition(Matrix A) + +
+          QR Decomposition, computed by Householder reflections.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ MatrixgetH() + +
+          Return the Householder vectors
+ MatrixgetQ() + +
+          Generate and return the (economy-sized) orthogonal factor
+ MatrixgetR() + +
+          Return the upper triangular factor
+ booleanisFullRank() + +
+          Is the matrix full rank?
+ Matrixsolve(Matrix B) + +
+          Least squares solution of A*X = B
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+QRDecomposition

+
+public QRDecomposition(Matrix A)
+
+
QR Decomposition, computed by Householder reflections. + Structure to access R and the Householder vectors and compute Q. +

+

+
Parameters:
A - Rectangular matrix
+
+ + + + + + + + +
+Method Detail
+ +

+isFullRank

+
+public boolean isFullRank()
+
+
Is the matrix full rank? +

+

+
+
+
+ +
Returns:
true if R, and hence A, has full rank.
+
+
+
+ +

+getH

+
+public Matrix getH()
+
+
Return the Householder vectors +

+

+
+
+
+ +
Returns:
Lower trapezoidal matrix whose columns define the reflections
+
+
+
+ +

+getR

+
+public Matrix getR()
+
+
Return the upper triangular factor +

+

+
+
+
+ +
Returns:
R
+
+
+
+ +

+getQ

+
+public Matrix getQ()
+
+
Generate and return the (economy-sized) orthogonal factor +

+

+
+
+
+ +
Returns:
Q
+
+
+
+ +

+solve

+
+public Matrix solve(Matrix B)
+
+
Least squares solution of A*X = B +

+

+
+
+
+
Parameters:
B - A Matrix with as many rows as A and any number of columns. +
Returns:
X that minimizes the two norm of Q*R*X-B. +
Throws: +
IllegalArgumentException - Matrix row dimensions must agree. +
RuntimeException - Matrix is rank deficient.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/SingularValueDecomposition.html b/Java/src/Jama/doc/Jama/SingularValueDecomposition.html new file mode 100644 index 00000000..a4e3b648 --- /dev/null +++ b/Java/src/Jama/doc/Jama/SingularValueDecomposition.html @@ -0,0 +1,430 @@ + + + + + + +SingularValueDecomposition + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ +

+ +Jama +
+Class SingularValueDecomposition

+
+java.lang.Object
+  extended by Jama.SingularValueDecomposition
+
+
+
All Implemented Interfaces:
Serializable
+
+
+
+
public class SingularValueDecomposition
extends Object
implements Serializable
+ + +

+Singular Value Decomposition. +

+ For an m-by-n matrix A with m >= n, the singular value decomposition is + an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and + an n-by-n orthogonal matrix V so that A = U*S*V'. +

+ The singular values, sigma[k] = S[k][k], are ordered so that + sigma[0] >= sigma[1] >= ... >= sigma[n-1]. +

+ The singular value decompostion always exists, so the constructor will + never fail. The matrix condition number and the effective numerical + rank can be computed from this decomposition. +

+ +

+

+
See Also:
Serialized Form
+
+ +

+ + + + + + + + + + + +
+Constructor Summary
SingularValueDecomposition(Matrix Arg) + +
+          Construct the singular value decomposition + Structure to access U, S and V.
+  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Method Summary
+ doublecond() + +
+          Two norm condition number
+ MatrixgetS() + +
+          Return the diagonal matrix of singular values
+ double[]getSingularValues() + +
+          Return the one-dimensional array of singular values
+ MatrixgetU() + +
+          Return the left singular vectors
+ MatrixgetV() + +
+          Return the right singular vectors
+ doublenorm2() + +
+          Two norm
+ intrank() + +
+          Effective numerical matrix rank
+ + + + + + + +
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
+  +

+ + + + + + + + +
+Constructor Detail
+ +

+SingularValueDecomposition

+
+public SingularValueDecomposition(Matrix Arg)
+
+
Construct the singular value decomposition + Structure to access U, S and V. +

+

+
Parameters:
Arg - Rectangular matrix
+
+ + + + + + + + +
+Method Detail
+ +

+getU

+
+public Matrix getU()
+
+
Return the left singular vectors +

+

+
+
+
+ +
Returns:
U
+
+
+
+ +

+getV

+
+public Matrix getV()
+
+
Return the right singular vectors +

+

+
+
+
+ +
Returns:
V
+
+
+
+ +

+getSingularValues

+
+public double[] getSingularValues()
+
+
Return the one-dimensional array of singular values +

+

+
+
+
+ +
Returns:
diagonal of S.
+
+
+
+ +

+getS

+
+public Matrix getS()
+
+
Return the diagonal matrix of singular values +

+

+
+
+
+ +
Returns:
S
+
+
+
+ +

+norm2

+
+public double norm2()
+
+
Two norm +

+

+
+
+
+ +
Returns:
max(S)
+
+
+
+ +

+cond

+
+public double cond()
+
+
Two norm condition number +

+

+
+
+
+ +
Returns:
max(S)/min(S)
+
+
+
+ +

+rank

+
+public int rank()
+
+
Effective numerical matrix rank +

+

+
+
+
+ +
Returns:
Number of nonnegligible singular values.
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/package-frame.html b/Java/src/Jama/doc/Jama/package-frame.html new file mode 100644 index 00000000..2176ac5d --- /dev/null +++ b/Java/src/Jama/doc/Jama/package-frame.html @@ -0,0 +1,42 @@ + + + + + + +Jama + + + + + + + + + + + +Jama + + + + +
+Classes  + +
+CholeskyDecomposition +
+EigenvalueDecomposition +
+LUDecomposition +
+Matrix +
+QRDecomposition +
+SingularValueDecomposition
+ + + + diff --git a/Java/src/Jama/doc/Jama/package-summary.html b/Java/src/Jama/doc/Jama/package-summary.html new file mode 100644 index 00000000..5f6906b9 --- /dev/null +++ b/Java/src/Jama/doc/Jama/package-summary.html @@ -0,0 +1,173 @@ + + + + + + +Jama + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+

+Package Jama +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Class Summary
CholeskyDecompositionCholesky Decomposition.
EigenvalueDecompositionEigenvalues and eigenvectors of a real matrix.
LUDecompositionLU Decomposition.
MatrixJama = Java Matrix class.
QRDecompositionQR Decomposition.
SingularValueDecompositionSingular Value Decomposition.
+  + +

+

+
+
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/Jama/package-tree.html b/Java/src/Jama/doc/Jama/package-tree.html new file mode 100644 index 00000000..91c8838e --- /dev/null +++ b/Java/src/Jama/doc/Jama/package-tree.html @@ -0,0 +1,152 @@ + + + + + + +Jama Class Hierarchy + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For Package Jama +

+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/allclasses-frame.html b/Java/src/Jama/doc/allclasses-frame.html new file mode 100644 index 00000000..0f941935 --- /dev/null +++ b/Java/src/Jama/doc/allclasses-frame.html @@ -0,0 +1,41 @@ + + + + + + +All Classes + + + + + + + + + + + +All Classes +
+ + + + + +
CholeskyDecomposition +
+EigenvalueDecomposition +
+LUDecomposition +
+Matrix +
+QRDecomposition +
+SingularValueDecomposition +
+
+ + + diff --git a/Java/src/Jama/doc/allclasses-noframe.html b/Java/src/Jama/doc/allclasses-noframe.html new file mode 100644 index 00000000..d999baac --- /dev/null +++ b/Java/src/Jama/doc/allclasses-noframe.html @@ -0,0 +1,41 @@ + + + + + + +All Classes + + + + + + + + + + + +All Classes +
+ + + + + +
CholeskyDecomposition +
+EigenvalueDecomposition +
+LUDecomposition +
+Matrix +
+QRDecomposition +
+SingularValueDecomposition +
+
+ + + diff --git a/Java/src/Jama/doc/constant-values.html b/Java/src/Jama/doc/constant-values.html new file mode 100644 index 00000000..8c4098d2 --- /dev/null +++ b/Java/src/Jama/doc/constant-values.html @@ -0,0 +1,142 @@ + + + + + + +Constant Field Values + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Constant Field Values

+
+
+Contents
    +
+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/deprecated-list.html b/Java/src/Jama/doc/deprecated-list.html new file mode 100644 index 00000000..9fb2aa23 --- /dev/null +++ b/Java/src/Jama/doc/deprecated-list.html @@ -0,0 +1,142 @@ + + + + + + +Deprecated List + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Deprecated API

+
+
+Contents
    +
+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/help-doc.html b/Java/src/Jama/doc/help-doc.html new file mode 100644 index 00000000..7c4b5fde --- /dev/null +++ b/Java/src/Jama/doc/help-doc.html @@ -0,0 +1,209 @@ + + + + + + +API Help + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+How This API Document Is Organized

+
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.

+Package

+
+ +

+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:

    +
  • Interfaces (italic)
  • Classes
  • Enums
  • Exceptions
  • Errors
  • Annotation Types
+
+

+Class/Interface

+
+ +

+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

    +
  • Class inheritance diagram
  • Direct Subclasses
  • All Known Subinterfaces
  • All Known Implementing Classes
  • Class/interface declaration
  • Class/interface description +

    +

  • Nested Class Summary
  • Field Summary
  • Constructor Summary
  • Method Summary +

    +

  • Field Detail
  • Constructor Detail
  • Method Detail
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+ +

+Annotation Type

+
+ +

+Each annotation type has its own separate page with the following sections:

    +
  • Annotation Type declaration
  • Annotation Type description
  • Required Element Summary
  • Optional Element Summary
  • Element Detail
+
+ +

+Enum

+
+ +

+Each enum has its own separate page with the following sections:

    +
  • Enum declaration
  • Enum description
  • Enum Constant Summary
  • Enum Constant Detail
+
+

+Tree (Class Hierarchy)

+
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
    +
  • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
  • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
+
+

+Deprecated API

+
+The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+

+Index

+
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+

+Prev/Next

+These links take you to the next or previous class, interface, package, or related page.

+Frames/No Frames

+These links show and hide the HTML frames. All pages are available with or without frames. +

+

+Serialized Form

+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description. +

+

+Constant Field Values

+The Constant Field Values page lists the static final fields and their values. +

+ + +This help file applies to API documentation generated using the standard doclet. + +
+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/index-all.html b/Java/src/Jama/doc/index-all.html new file mode 100644 index 00000000..93b37ef7 --- /dev/null +++ b/Java/src/Jama/doc/index-all.html @@ -0,0 +1,495 @@ + + + + + + +Index + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +A C D E G I J L M N P Q R S T U
+

+A

+
+
arrayLeftDivide(Matrix) - +Method in class Jama.Matrix +
Element-by-element left division, C = A.\B +
arrayLeftDivideEquals(Matrix) - +Method in class Jama.Matrix +
Element-by-element left division in place, A = A.\B +
arrayRightDivide(Matrix) - +Method in class Jama.Matrix +
Element-by-element right division, C = A./B +
arrayRightDivideEquals(Matrix) - +Method in class Jama.Matrix +
Element-by-element right division in place, A = A./B +
arrayTimes(Matrix) - +Method in class Jama.Matrix +
Element-by-element multiplication, C = A.*B +
arrayTimesEquals(Matrix) - +Method in class Jama.Matrix +
Element-by-element multiplication in place, A = A.*B +
+
+

+C

+
+
chol() - +Method in class Jama.Matrix +
Cholesky Decomposition +
CholeskyDecomposition - Class in Jama
Cholesky Decomposition.
CholeskyDecomposition(Matrix) - +Constructor for class Jama.CholeskyDecomposition +
Cholesky algorithm for symmetric and positive definite matrix. +
clone() - +Method in class Jama.Matrix +
Clone the Matrix object. +
cond() - +Method in class Jama.Matrix +
Matrix condition (2 norm) +
cond() - +Method in class Jama.SingularValueDecomposition +
Two norm condition number +
constructWithCopy(double[][]) - +Static method in class Jama.Matrix +
Construct a matrix from a copy of a 2-D array. +
copy() - +Method in class Jama.Matrix +
Make a deep copy of a matrix +
+
+

+D

+
+
det() - +Method in class Jama.LUDecomposition +
Determinant +
det() - +Method in class Jama.Matrix +
Matrix determinant +
+
+

+E

+
+
eig() - +Method in class Jama.Matrix +
Eigenvalue Decomposition +
EigenvalueDecomposition - Class in Jama
Eigenvalues and eigenvectors of a real matrix.
EigenvalueDecomposition(Matrix) - +Constructor for class Jama.EigenvalueDecomposition +
Check for symmetry, then construct the eigenvalue decomposition + Structure to access D and V. +
+
+

+G

+
+
get(int, int) - +Method in class Jama.Matrix +
Get a single element. +
getArray() - +Method in class Jama.Matrix +
Access the internal two-dimensional array. +
getArrayCopy() - +Method in class Jama.Matrix +
Copy the internal two-dimensional array. +
getColumnDimension() - +Method in class Jama.Matrix +
Get column dimension. +
getColumnPackedCopy() - +Method in class Jama.Matrix +
Make a one-dimensional column packed copy of the internal array. +
getD() - +Method in class Jama.EigenvalueDecomposition +
Return the block diagonal eigenvalue matrix +
getDoublePivot() - +Method in class Jama.LUDecomposition +
Return pivot permutation vector as a one-dimensional double array +
getH() - +Method in class Jama.QRDecomposition +
Return the Householder vectors +
getImagEigenvalues() - +Method in class Jama.EigenvalueDecomposition +
Return the imaginary parts of the eigenvalues +
getL() - +Method in class Jama.CholeskyDecomposition +
Return triangular factor. +
getL() - +Method in class Jama.LUDecomposition +
Return lower triangular factor +
getMatrix(int, int, int, int) - +Method in class Jama.Matrix +
Get a submatrix. +
getMatrix(int[], int[]) - +Method in class Jama.Matrix +
Get a submatrix. +
getMatrix(int, int, int[]) - +Method in class Jama.Matrix +
Get a submatrix. +
getMatrix(int[], int, int) - +Method in class Jama.Matrix +
Get a submatrix. +
getPivot() - +Method in class Jama.LUDecomposition +
Return pivot permutation vector +
getQ() - +Method in class Jama.QRDecomposition +
Generate and return the (economy-sized) orthogonal factor +
getR() - +Method in class Jama.QRDecomposition +
Return the upper triangular factor +
getRealEigenvalues() - +Method in class Jama.EigenvalueDecomposition +
Return the real parts of the eigenvalues +
getRowDimension() - +Method in class Jama.Matrix +
Get row dimension. +
getRowPackedCopy() - +Method in class Jama.Matrix +
Make a one-dimensional row packed copy of the internal array. +
getS() - +Method in class Jama.SingularValueDecomposition +
Return the diagonal matrix of singular values +
getSingularValues() - +Method in class Jama.SingularValueDecomposition +
Return the one-dimensional array of singular values +
getU() - +Method in class Jama.LUDecomposition +
Return upper triangular factor +
getU() - +Method in class Jama.SingularValueDecomposition +
Return the left singular vectors +
getV() - +Method in class Jama.EigenvalueDecomposition +
Return the eigenvector matrix +
getV() - +Method in class Jama.SingularValueDecomposition +
Return the right singular vectors +
+
+

+I

+
+
identity(int, int) - +Static method in class Jama.Matrix +
Generate identity matrix +
inverse() - +Method in class Jama.Matrix +
Matrix inverse or pseudoinverse +
isFullRank() - +Method in class Jama.QRDecomposition +
Is the matrix full rank? +
isNonsingular() - +Method in class Jama.LUDecomposition +
Is the matrix nonsingular? +
isSPD() - +Method in class Jama.CholeskyDecomposition +
Is the matrix symmetric and positive definite? +
+
+

+J

+
+
Jama - package Jama
 
+
+

+L

+
+
lu() - +Method in class Jama.Matrix +
LU Decomposition +
LUDecomposition - Class in Jama
LU Decomposition.
LUDecomposition(Matrix) - +Constructor for class Jama.LUDecomposition +
LU Decomposition + Structure to access L, U and piv. +
+
+

+M

+
+
Matrix - Class in Jama
Jama = Java Matrix class.
Matrix(int, int) - +Constructor for class Jama.Matrix +
Construct an m-by-n matrix of zeros. +
Matrix(int, int, double) - +Constructor for class Jama.Matrix +
Construct an m-by-n constant matrix. +
Matrix(double[][]) - +Constructor for class Jama.Matrix +
Construct a matrix from a 2-D array. +
Matrix(double[][], int, int) - +Constructor for class Jama.Matrix +
Construct a matrix quickly without checking arguments. +
Matrix(double[], int) - +Constructor for class Jama.Matrix +
Construct a matrix from a one-dimensional packed array +
minus(Matrix) - +Method in class Jama.Matrix +
C = A - B +
minusEquals(Matrix) - +Method in class Jama.Matrix +
A = A - B +
+
+

+N

+
+
norm1() - +Method in class Jama.Matrix +
One norm +
norm2() - +Method in class Jama.Matrix +
Two norm +
norm2() - +Method in class Jama.SingularValueDecomposition +
Two norm +
normF() - +Method in class Jama.Matrix +
Frobenius norm +
normInf() - +Method in class Jama.Matrix +
Infinity norm +
+
+

+P

+
+
plus(Matrix) - +Method in class Jama.Matrix +
C = A + B +
plusEquals(Matrix) - +Method in class Jama.Matrix +
A = A + B +
print(int, int) - +Method in class Jama.Matrix +
Print the matrix to stdout. +
print(PrintWriter, int, int) - +Method in class Jama.Matrix +
Print the matrix to the output stream. +
print(NumberFormat, int) - +Method in class Jama.Matrix +
Print the matrix to stdout. +
print(PrintWriter, NumberFormat, int) - +Method in class Jama.Matrix +
Print the matrix to the output stream. +
+
+

+Q

+
+
qr() - +Method in class Jama.Matrix +
QR Decomposition +
QRDecomposition - Class in Jama
QR Decomposition.
QRDecomposition(Matrix) - +Constructor for class Jama.QRDecomposition +
QR Decomposition, computed by Householder reflections. +
+
+

+R

+
+
random(int, int) - +Static method in class Jama.Matrix +
Generate matrix with random elements +
rank() - +Method in class Jama.Matrix +
Matrix rank +
rank() - +Method in class Jama.SingularValueDecomposition +
Effective numerical matrix rank +
read(BufferedReader) - +Static method in class Jama.Matrix +
Read a matrix from a stream. +
+
+

+S

+
+
set(int, int, double) - +Method in class Jama.Matrix +
Set a single element. +
setMatrix(int, int, int, int, Matrix) - +Method in class Jama.Matrix +
Set a submatrix. +
setMatrix(int[], int[], Matrix) - +Method in class Jama.Matrix +
Set a submatrix. +
setMatrix(int[], int, int, Matrix) - +Method in class Jama.Matrix +
Set a submatrix. +
setMatrix(int, int, int[], Matrix) - +Method in class Jama.Matrix +
Set a submatrix. +
SingularValueDecomposition - Class in Jama
Singular Value Decomposition.
SingularValueDecomposition(Matrix) - +Constructor for class Jama.SingularValueDecomposition +
Construct the singular value decomposition + Structure to access U, S and V. +
solve(Matrix) - +Method in class Jama.CholeskyDecomposition +
Solve A*X = B +
solve(Matrix) - +Method in class Jama.LUDecomposition +
Solve A*X = B +
solve(Matrix) - +Method in class Jama.Matrix +
Solve A*X = B +
solve(Matrix) - +Method in class Jama.QRDecomposition +
Least squares solution of A*X = B +
solveTranspose(Matrix) - +Method in class Jama.Matrix +
Solve X*A = B, which is also A'*X' = B' +
svd() - +Method in class Jama.Matrix +
Singular Value Decomposition +
+
+

+T

+
+
times(double) - +Method in class Jama.Matrix +
Multiply a matrix by a scalar, C = s*A +
times(Matrix) - +Method in class Jama.Matrix +
Linear algebraic matrix multiplication, A * B +
timesEquals(double) - +Method in class Jama.Matrix +
Multiply a matrix by a scalar in place, A = s*A +
trace() - +Method in class Jama.Matrix +
Matrix trace. +
transpose() - +Method in class Jama.Matrix +
Matrix transpose. +
+
+

+U

+
+
uminus() - +Method in class Jama.Matrix +
Unary minus +
+
+A C D E G I J L M N P Q R S T U + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/index.html b/Java/src/Jama/doc/index.html new file mode 100644 index 00000000..4178db8b --- /dev/null +++ b/Java/src/Jama/doc/index.html @@ -0,0 +1,36 @@ + + + + + + +Generated Documentation (Untitled) + + + + + + + + +<H2> +Frame Alert</H2> + +<P> +This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. +<BR> +Link to<A HREF="Jama/package-summary.html">Non-frame version.</A> + + + diff --git a/Java/src/Jama/doc/overview-tree.html b/Java/src/Jama/doc/overview-tree.html new file mode 100644 index 00000000..007b6f59 --- /dev/null +++ b/Java/src/Jama/doc/overview-tree.html @@ -0,0 +1,154 @@ + + + + + + +Class Hierarchy + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Hierarchy For All Packages

+
+
+
Package Hierarchies:
Jama
+
+

+Class Hierarchy +

+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/package-list b/Java/src/Jama/doc/package-list new file mode 100644 index 00000000..05110bdf --- /dev/null +++ b/Java/src/Jama/doc/package-list @@ -0,0 +1 @@ +Jama diff --git a/Java/src/Jama/doc/resources/inherit.gif b/Java/src/Jama/doc/resources/inherit.gif new file mode 100644 index 00000000..c814867a Binary files /dev/null and b/Java/src/Jama/doc/resources/inherit.gif differ diff --git a/Java/src/Jama/doc/serialized-form.html b/Java/src/Jama/doc/serialized-form.html new file mode 100644 index 00000000..2ab16a22 --- /dev/null +++ b/Java/src/Jama/doc/serialized-form.html @@ -0,0 +1,571 @@ + + + + + + +Serialized Form + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + +
+
+

+Serialized Form

+
+
+ + + + + +
+Package Jama
+ +

+ + + + + +
+Class Jama.CholeskyDecomposition extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+L

+
+double[][] L
+
+
Array for internal storage of decomposition. +

+

internal array storage.
+
+
+
+

+n

+
+int n
+
+
Row and column dimension (square matrix). +

+

matrix dimension.
+
+
+
+

+isspd

+
+boolean isspd
+
+
Symmetric and positive definite flag. +

+

is symmetric and positive definite flag.
+
+
+ +

+ + + + + +
+Class Jama.EigenvalueDecomposition extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+n

+
+int n
+
+
Row and column dimension (square matrix). +

+

matrix dimension.
+
+
+
+

+issymmetric

+
+boolean issymmetric
+
+
Symmetry flag. +

+

internal symmetry flag.
+
+
+
+

+d

+
+double[] d
+
+
Arrays for internal storage of eigenvalues. +

+

internal storage of eigenvalues.
+
+
+
+

+e

+
+double[] e
+
+
Arrays for internal storage of eigenvalues. +

+

internal storage of eigenvalues.
+
+
+
+

+V

+
+double[][] V
+
+
Array for internal storage of eigenvectors. +

+

internal storage of eigenvectors.
+
+
+
+

+H

+
+double[][] H
+
+
Array for internal storage of nonsymmetric Hessenberg form. +

+

internal storage of nonsymmetric Hessenberg form.
+
+
+
+

+ort

+
+double[] ort
+
+
Working storage for nonsymmetric algorithm. +

+

working storage for nonsymmetric algorithm.
+
+
+ +

+ + + + + +
+Class Jama.LUDecomposition extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+LU

+
+double[][] LU
+
+
Array for internal storage of decomposition. +

+

internal array storage.
+
+
+
+

+m

+
+int m
+
+
Row and column dimensions, and pivot sign. +

+

column dimension.
+
+
+
+

+n

+
+int n
+
+
Row and column dimensions, and pivot sign. +

+

column dimension.
+
+
+
+

+pivsign

+
+int pivsign
+
+
Row and column dimensions, and pivot sign. +

+

column dimension.
+
+
+
+

+piv

+
+int[] piv
+
+
Internal storage of pivot vector. +

+

pivot vector.
+
+
+ +

+ + + + + +
+Class Jama.Matrix extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+A

+
+double[][] A
+
+
Array for internal storage of elements. +

+

internal array storage.
+
+
+
+

+m

+
+int m
+
+
Row and column dimensions. +

+

row dimension.
+
+
+
+

+n

+
+int n
+
+
Row and column dimensions. +

+

row dimension.
+
+
+ +

+ + + + + +
+Class Jama.QRDecomposition extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+QR

+
+double[][] QR
+
+
Array for internal storage of decomposition. +

+

internal array storage.
+
+
+
+

+m

+
+int m
+
+
Row and column dimensions. +

+

column dimension.
+
+
+
+

+n

+
+int n
+
+
Row and column dimensions. +

+

column dimension.
+
+
+
+

+Rdiag

+
+double[] Rdiag
+
+
Array for internal storage of diagonal of R. +

+

diagonal of R.
+
+
+ +

+ + + + + +
+Class Jama.SingularValueDecomposition extends Object implements Serializable
+ +

+serialVersionUID: 1L + +

+ + + + + +
+Serialized Fields
+ +

+U

+
+double[][] U
+
+
Arrays for internal storage of U and V. +

+

internal storage of U.
+
+
+
+

+V

+
+double[][] V
+
+
Arrays for internal storage of U and V. +

+

internal storage of U.
+
+
+
+

+s

+
+double[] s
+
+
Array for internal storage of singular values. +

+

internal storage of singular values.
+
+
+
+

+m

+
+int m
+
+
Row and column dimensions. +

+

row dimension.
+
+
+
+

+n

+
+int n
+
+
Row and column dimensions. +

+

row dimension.
+
+
+ +

+


+ + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + diff --git a/Java/src/Jama/doc/stylesheet.css b/Java/src/Jama/doc/stylesheet.css new file mode 100644 index 00000000..6ea9e516 --- /dev/null +++ b/Java/src/Jama/doc/stylesheet.css @@ -0,0 +1,29 @@ +/* Javadoc style sheet */ + +/* Define colors, fonts and other style attributes here to override the defaults */ + +/* Page background color */ +body { background-color: #FFFFFF; color:#000000 } + +/* Headings */ +h1 { font-size: 145% } + +/* Table colors */ +.TableHeadingColor { background: #CCCCFF; color:#000000 } /* Dark mauve */ +.TableSubHeadingColor { background: #EEEEFF; color:#000000 } /* Light mauve */ +.TableRowColor { background: #FFFFFF; color:#000000 } /* White */ + +/* Font used in left-hand frame lists */ +.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } + +/* Navigation bar fonts and colors */ +.NavBarCell1 { background-color:#EEEEFF; color:#000000} /* Light mauve */ +.NavBarCell1Rev { background-color:#00008B; color:#FFFFFF} /* Dark Blue */ +.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;color:#000000;} +.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;color:#FFFFFF;} + +.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000} +.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000} + diff --git a/Java/src/Jama/examples/MagicSquareExample.java b/Java/src/Jama/examples/MagicSquareExample.java new file mode 100644 index 00000000..cf0b7db9 --- /dev/null +++ b/Java/src/Jama/examples/MagicSquareExample.java @@ -0,0 +1,163 @@ +package Jama.examples; +import Jama.*; +import java.util.Date; + +/** Example of use of Matrix Class, featuring magic squares. **/ + +public class MagicSquareExample { + + /** Generate magic square test matrix. **/ + + public static Matrix magic (int n) { + + double[][] M = new double[n][n]; + + // Odd order + + if ((n % 2) == 1) { + int a = (n+1)/2; + int b = (n+1); + for (int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + M[i][j] = n*((i+j+a) % n) + ((i+2*j+b) % n) + 1; + } + } + + // Doubly Even Order + + } else if ((n % 4) == 0) { + for (int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + if (((i+1)/2)%2 == ((j+1)/2)%2) { + M[i][j] = n*n-n*i-j; + } else { + M[i][j] = n*i+j+1; + } + } + } + + // Singly Even Order + + } else { + int p = n/2; + int k = (n-2)/4; + Matrix A = magic(p); + for (int j = 0; j < p; j++) { + for (int i = 0; i < p; i++) { + double aij = A.get(i,j); + M[i][j] = aij; + M[i][j+p] = aij + 2*p*p; + M[i+p][j] = aij + 3*p*p; + M[i+p][j+p] = aij + p*p; + } + } + for (int i = 0; i < p; i++) { + for (int j = 0; j < k; j++) { + double t = M[i][j]; M[i][j] = M[i+p][j]; M[i+p][j] = t; + } + for (int j = n-k+1; j < n; j++) { + double t = M[i][j]; M[i][j] = M[i+p][j]; M[i+p][j] = t; + } + } + double t = M[k][0]; M[k][0] = M[k+p][0]; M[k+p][0] = t; + t = M[k][k]; M[k][k] = M[k+p][k]; M[k+p][k] = t; + } + return new Matrix(M); + } + + /** Shorten spelling of print. **/ + + private static void print (String s) { + System.out.print(s); + } + + /** Format double with Fw.d. **/ + + public static String fixedWidthDoubletoString (double x, int w, int d) { + java.text.DecimalFormat fmt = new java.text.DecimalFormat(); + fmt.setMaximumFractionDigits(d); + fmt.setMinimumFractionDigits(d); + fmt.setGroupingUsed(false); + String s = fmt.format(x); + while (s.length() < w) { + s = " " + s; + } + return s; + } + + /** Format integer with Iw. **/ + + public static String fixedWidthIntegertoString (int n, int w) { + String s = Integer.toString(n); + while (s.length() < w) { + s = " " + s; + } + return s; + } + + + public static void main (String argv[]) { + + /* + | Tests LU, QR, SVD and symmetric Eig decompositions. + | + | n = order of magic square. + | trace = diagonal sum, should be the magic sum, (n^3 + n)/2. + | max_eig = maximum eigenvalue of (A + A')/2, should equal trace. + | rank = linear algebraic rank, + | should equal n if n is odd, be less than n if n is even. + | cond = L_2 condition number, ratio of singular values. + | lu_res = test of LU factorization, norm1(L*U-A(p,:))/(n*eps). + | qr_res = test of QR factorization, norm1(Q*R-A)/(n*eps). + */ + + print("\n Test of Matrix Class, using magic squares.\n"); + print(" See MagicSquareExample.main() for an explanation.\n"); + print("\n n trace max_eig rank cond lu_res qr_res\n\n"); + + Date start_time = new Date(); + double eps = Math.pow(2.0,-52.0); + for (int n = 3; n <= 32; n++) { + print(fixedWidthIntegertoString(n,7)); + + Matrix M = magic(n); + + int t = (int) M.trace(); + print(fixedWidthIntegertoString(t,10)); + + EigenvalueDecomposition E = + new EigenvalueDecomposition(M.plus(M.transpose()).times(0.5)); + double[] d = E.getRealEigenvalues(); + print(fixedWidthDoubletoString(d[n-1],14,3)); + + int r = M.rank(); + print(fixedWidthIntegertoString(r,7)); + + double c = M.cond(); + print(c < 1/eps ? fixedWidthDoubletoString(c,12,3) : + " Inf"); + + LUDecomposition LU = new LUDecomposition(M); + Matrix L = LU.getL(); + Matrix U = LU.getU(); + int[] p = LU.getPivot(); + Matrix R = L.times(U).minus(M.getMatrix(p,0,n-1)); + double res = R.norm1()/(n*eps); + print(fixedWidthDoubletoString(res,12,3)); + + QRDecomposition QR = new QRDecomposition(M); + Matrix Q = QR.getQ(); + R = QR.getR(); + R = Q.times(R).minus(M); + res = R.norm1()/(n*eps); + print(fixedWidthDoubletoString(res,12,3)); + + print("\n"); + } + Date stop_time = new Date(); + double etime = (stop_time.getTime() - start_time.getTime())/1000.; + print("\nElapsed Time = " + + fixedWidthDoubletoString(etime,12,3) + " seconds\n"); + print("Adios\n"); + } +} diff --git a/Java/src/Jama/test/TestMatrix.java b/Java/src/Jama/test/TestMatrix.java new file mode 100644 index 00000000..d08ca2c4 --- /dev/null +++ b/Java/src/Jama/test/TestMatrix.java @@ -0,0 +1,957 @@ +package Jama.test; +import Jama.*; +import java.io.*; +import java.util.zip.GZIPInputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +/** TestMatrix tests the functionality of the Jama Matrix class and associated decompositions. +

+Run the test from the command line using +


+ java Jama.test.TestMatrix 
+
+Detailed output is provided indicating the functionality being tested +and whether the functionality is correctly implemented. Exception handling +is also tested. +

+The test is designed to run to completion and give a summary of any implementation errors +encountered. The final output should be: +


+      TestMatrix completed.
+      Total errors reported: n1
+      Total warning reported: n2
+
+If the test does not run to completion, this indicates that there is a +substantial problem within the implementation that was not anticipated in the test design. +The stopping point should give an indication of where the problem exists. +**/ +public class TestMatrix { + public static void main (String argv[]) { + Matrix A,B,C,Z,O,I,R,S,X,SUB,M,T,SQ,DEF,SOL; + // Uncomment this to test IO in a different locale. + // Locale.setDefault(Locale.GERMAN); + int errorCount=0; + int warningCount=0; + double tmp, s; + double[] columnwise = {1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.}; + double[] rowwise = {1.,4.,7.,10.,2.,5.,8.,11.,3.,6.,9.,12.}; + double[][] avals = {{1.,4.,7.,10.},{2.,5.,8.,11.},{3.,6.,9.,12.}}; + double[][] rankdef = avals; + double[][] tvals = {{1.,2.,3.},{4.,5.,6.},{7.,8.,9.},{10.,11.,12.}}; + double[][] subavals = {{5.,8.,11.},{6.,9.,12.}}; + double[][] rvals = {{1.,4.,7.},{2.,5.,8.,11.},{3.,6.,9.,12.}}; + double[][] pvals = {{4.,1.,1.},{1.,2.,3.},{1.,3.,6.}}; + double[][] ivals = {{1.,0.,0.,0.},{0.,1.,0.,0.},{0.,0.,1.,0.}}; + double[][] evals = + {{0.,1.,0.,0.},{1.,0.,2.e-7,0.},{0.,-2.e-7,0.,1.},{0.,0.,1.,0.}}; + double[][] square = {{166.,188.,210.},{188.,214.,240.},{210.,240.,270.}}; + double[][] sqSolution = {{13.},{15.}}; + double[][] condmat = {{1.,3.},{7.,9.}}; + double[][] badeigs = {{0,0,0,0,0}, {0,0,0,0,1},{0,0,0,1,0}, + {1,1,0,0,1},{1,0,1,0,1}}; + int rows=3,cols=4; + int invalidld=5;/* should trigger bad shape for construction with val */ + int raggedr=0; /* (raggedr,raggedc) should be out of bounds in ragged array */ + int raggedc=4; + int validld=3; /* leading dimension of intended test Matrices */ + int nonconformld=4; /* leading dimension which is valid, but nonconforming */ + int ib=1,ie=2,jb=1,je=3; /* index ranges for sub Matrix */ + int[] rowindexset = {1,2}; + int[] badrowindexset = {1,3}; + int[] columnindexset = {1,2,3}; + int[] badcolumnindexset = {1,2,4}; + double columnsummax = 33.; + double rowsummax = 30.; + double sumofdiagonals = 15; + double sumofsquares = 650; + +/** + Constructors and constructor-like methods: + double[], int + double[][] + int, int + int, int, double + int, int, double[][] + constructWithCopy(double[][]) + random(int,int) + identity(int) +**/ + + print("\nTesting constructors and constructor-like methods...\n"); + try{ + /** check that exception is thrown in packed constructor with invalid length **/ + A = new Matrix(columnwise,invalidld); + errorCount = try_failure(errorCount,"Catch invalid length in packed constructor... ", + "exception not thrown for invalid input"); + } catch ( IllegalArgumentException e ) { + try_success("Catch invalid length in packed constructor... ", + e.getMessage()); + } + try{ + /** check that exception is thrown in default constructor + if input array is 'ragged' **/ + A = new Matrix(rvals); + tmp = A.get(raggedr,raggedc); + } catch ( IllegalArgumentException e ) { + try_success("Catch ragged input to default constructor... ", + e.getMessage()); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"Catch ragged input to constructor... ", + "exception not thrown in construction...ArrayIndexOutOfBoundsException thrown later"); + } + try{ + /** check that exception is thrown in constructWithCopy + if input array is 'ragged' **/ + A = Matrix.constructWithCopy(rvals); + tmp = A.get(raggedr,raggedc); + } catch ( IllegalArgumentException e ) { + try_success("Catch ragged input to constructWithCopy... ",e.getMessage()); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"Catch ragged input to constructWithCopy... ","exception not thrown in construction...ArrayIndexOutOfBoundsException thrown later"); + } + + A = new Matrix(columnwise,validld); + B = new Matrix(avals); + tmp = B.get(0,0); + avals[0][0] = 0.0; + C = B.minus(A); + avals[0][0] = tmp; + B = Matrix.constructWithCopy(avals); + tmp = B.get(0,0); + avals[0][0] = 0.0; + if ( ( tmp - B.get(0,0) ) != 0.0 ) { + /** check that constructWithCopy behaves properly **/ + errorCount = try_failure(errorCount,"constructWithCopy... ","copy not effected... data visible outside"); + } else { + try_success("constructWithCopy... ",""); + } + avals[0][0] = columnwise[0]; + I = new Matrix(ivals); + try { + check(I,Matrix.identity(3,4)); + try_success("identity... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"identity... ","identity Matrix not successfully created"); + } + +/** + Access Methods: + getColumnDimension() + getRowDimension() + getArray() + getArrayCopy() + getColumnPackedCopy() + getRowPackedCopy() + get(int,int) + getMatrix(int,int,int,int) + getMatrix(int,int,int[]) + getMatrix(int[],int,int) + getMatrix(int[],int[]) + set(int,int,double) + setMatrix(int,int,int,int,Matrix) + setMatrix(int,int,int[],Matrix) + setMatrix(int[],int,int,Matrix) + setMatrix(int[],int[],Matrix) +**/ + + print("\nTesting access methods...\n"); + +/** + Various get methods: +**/ + + B = new Matrix(avals); + if (B.getRowDimension() != rows) { + errorCount = try_failure(errorCount,"getRowDimension... ",""); + } else { + try_success("getRowDimension... ",""); + } + if (B.getColumnDimension() != cols) { + errorCount = try_failure(errorCount,"getColumnDimension... ",""); + } else { + try_success("getColumnDimension... ",""); + } + B = new Matrix(avals); + double[][] barray = B.getArray(); + if ( barray != avals ) { + errorCount = try_failure(errorCount,"getArray... ",""); + } else { + try_success("getArray... ",""); + } + barray = B.getArrayCopy(); + if ( barray == avals ) { + errorCount = try_failure(errorCount,"getArrayCopy... ","data not (deep) copied"); + } + try { + check(barray,avals); + try_success("getArrayCopy... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getArrayCopy... ","data not successfully (deep) copied"); + } + double[] bpacked = B.getColumnPackedCopy(); + try { + check(bpacked,columnwise); + try_success("getColumnPackedCopy... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getColumnPackedCopy... ","data not successfully (deep) copied by columns"); + } + bpacked = B.getRowPackedCopy(); + try { + check(bpacked,rowwise); + try_success("getRowPackedCopy... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getRowPackedCopy... ","data not successfully (deep) copied by rows"); + } + try { + tmp = B.get(B.getRowDimension(),B.getColumnDimension()-1); + errorCount = try_failure(errorCount,"get(int,int)... ","OutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + tmp = B.get(B.getRowDimension()-1,B.getColumnDimension()); + errorCount = try_failure(errorCount,"get(int,int)... ","OutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("get(int,int)... OutofBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"get(int,int)... ","OutOfBoundsException expected but not thrown"); + } + try { + if (B.get(B.getRowDimension()-1,B.getColumnDimension()-1) != + avals[B.getRowDimension()-1][B.getColumnDimension()-1] ) { + errorCount = try_failure(errorCount,"get(int,int)... ","Matrix entry (i,j) not successfully retreived"); + } else { + try_success("get(int,int)... ",""); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"get(int,int)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + SUB = new Matrix(subavals); + try { + M = B.getMatrix(ib,ie+B.getRowDimension()+1,jb,je); + errorCount = try_failure(errorCount,"getMatrix(int,int,int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + M = B.getMatrix(ib,ie,jb,je+B.getColumnDimension()+1); + errorCount = try_failure(errorCount,"getMatrix(int,int,int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("getMatrix(int,int,int,int)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + M = B.getMatrix(ib,ie,jb,je); + try { + check(SUB,M); + try_success("getMatrix(int,int,int,int)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int,int)... ","submatrix not successfully retreived"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int,int)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + + try { + M = B.getMatrix(ib,ie,badcolumnindexset); + errorCount = try_failure(errorCount,"getMatrix(int,int,int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + M = B.getMatrix(ib,ie+B.getRowDimension()+1,columnindexset); + errorCount = try_failure(errorCount,"getMatrix(int,int,int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("getMatrix(int,int,int[])... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + M = B.getMatrix(ib,ie,columnindexset); + try { + check(SUB,M); + try_success("getMatrix(int,int,int[])... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int[])... ","submatrix not successfully retreived"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"getMatrix(int,int,int[])... ","Unexpected ArrayIndexOutOfBoundsException"); + } + try { + M = B.getMatrix(badrowindexset,jb,je); + errorCount = try_failure(errorCount,"getMatrix(int[],int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + M = B.getMatrix(rowindexset,jb,je+B.getColumnDimension()+1); + errorCount = try_failure(errorCount,"getMatrix(int[],int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("getMatrix(int[],int,int)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int,int)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + M = B.getMatrix(rowindexset,jb,je); + try { + check(SUB,M); + try_success("getMatrix(int[],int,int)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int,int)... ","submatrix not successfully retreived"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int,int)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + try { + M = B.getMatrix(badrowindexset,columnindexset); + errorCount = try_failure(errorCount,"getMatrix(int[],int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + M = B.getMatrix(rowindexset,badcolumnindexset); + errorCount = try_failure(errorCount,"getMatrix(int[],int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("getMatrix(int[],int[])... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int[])... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + M = B.getMatrix(rowindexset,columnindexset); + try { + check(SUB,M); + try_success("getMatrix(int[],int[])... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int[])... ","submatrix not successfully retreived"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + errorCount = try_failure(errorCount,"getMatrix(int[],int[])... ","Unexpected ArrayIndexOutOfBoundsException"); + } + +/** + Various set methods: +**/ + + try { + B.set(B.getRowDimension(),B.getColumnDimension()-1,0.); + errorCount = try_failure(errorCount,"set(int,int,double)... ","OutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + B.set(B.getRowDimension()-1,B.getColumnDimension(),0.); + errorCount = try_failure(errorCount,"set(int,int,double)... ","OutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("set(int,int,double)... OutofBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"set(int,int,double)... ","OutOfBoundsException expected but not thrown"); + } + try { + B.set(ib,jb,0.); + tmp = B.get(ib,jb); + try { + check(tmp,0.); + try_success("set(int,int,double)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"set(int,int,double)... ","Matrix element not successfully set"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e1) { + errorCount = try_failure(errorCount,"set(int,int,double)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + M = new Matrix(2,3,0.); + try { + B.setMatrix(ib,ie+B.getRowDimension()+1,jb,je,M); + errorCount = try_failure(errorCount,"setMatrix(int,int,int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + B.setMatrix(ib,ie,jb,je+B.getColumnDimension()+1,M); + errorCount = try_failure(errorCount,"setMatrix(int,int,int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("setMatrix(int,int,int,int,Matrix)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + B.setMatrix(ib,ie,jb,je,M); + try { + check(M.minus(B.getMatrix(ib,ie,jb,je)),M); + try_success("setMatrix(int,int,int,int,Matrix)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int,int,Matrix)... ","submatrix not successfully set"); + } + B.setMatrix(ib,ie,jb,je,SUB); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int,int,Matrix)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + try { + B.setMatrix(ib,ie+B.getRowDimension()+1,columnindexset,M); + errorCount = try_failure(errorCount,"setMatrix(int,int,int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + B.setMatrix(ib,ie,badcolumnindexset,M); + errorCount = try_failure(errorCount,"setMatrix(int,int,int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("setMatrix(int,int,int[],Matrix)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + B.setMatrix(ib,ie,columnindexset,M); + try { + check(M.minus(B.getMatrix(ib,ie,columnindexset)),M); + try_success("setMatrix(int,int,int[],Matrix)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int[],Matrix)... ","submatrix not successfully set"); + } + B.setMatrix(ib,ie,jb,je,SUB); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int,int,int[],Matrix)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + try { + B.setMatrix(rowindexset,jb,je+B.getColumnDimension()+1,M); + errorCount = try_failure(errorCount,"setMatrix(int[],int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + B.setMatrix(badrowindexset,jb,je,M); + errorCount = try_failure(errorCount,"setMatrix(int[],int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("setMatrix(int[],int,int,Matrix)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int,int,Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + B.setMatrix(rowindexset,jb,je,M); + try { + check(M.minus(B.getMatrix(rowindexset,jb,je)),M); + try_success("setMatrix(int[],int,int,Matrix)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int,int,Matrix)... ","submatrix not successfully set"); + } + B.setMatrix(ib,ie,jb,je,SUB); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int,int,Matrix)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + try { + B.setMatrix(rowindexset,badcolumnindexset,M); + errorCount = try_failure(errorCount,"setMatrix(int[],int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e ) { + try { + B.setMatrix(badrowindexset,columnindexset,M); + errorCount = try_failure(errorCount,"setMatrix(int[],int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + try_success("setMatrix(int[],int[],Matrix)... ArrayIndexOutOfBoundsException... ",""); + } + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int[],Matrix)... ","ArrayIndexOutOfBoundsException expected but not thrown"); + } + try { + B.setMatrix(rowindexset,columnindexset,M); + try { + check(M.minus(B.getMatrix(rowindexset,columnindexset)),M); + try_success("setMatrix(int[],int[],Matrix)... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int[],Matrix)... ","submatrix not successfully set"); + } + } catch ( java.lang.ArrayIndexOutOfBoundsException e1 ) { + errorCount = try_failure(errorCount,"setMatrix(int[],int[],Matrix)... ","Unexpected ArrayIndexOutOfBoundsException"); + } + +/** + Array-like methods: + minus + minusEquals + plus + plusEquals + arrayLeftDivide + arrayLeftDivideEquals + arrayRightDivide + arrayRightDivideEquals + arrayTimes + arrayTimesEquals + uminus +**/ + + print("\nTesting array-like methods...\n"); + S = new Matrix(columnwise,nonconformld); + R = Matrix.random(A.getRowDimension(),A.getColumnDimension()); + A = R; + try { + S = A.minus(S); + errorCount = try_failure(errorCount,"minus conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("minus conformance check... ",""); + } + if (A.minus(R).norm1() != 0.) { + errorCount = try_failure(errorCount,"minus... ","(difference of identical Matrices is nonzero,\nSubsequent use of minus should be suspect)"); + } else { + try_success("minus... ",""); + } + A = R.copy(); + A.minusEquals(R); + Z = new Matrix(A.getRowDimension(),A.getColumnDimension()); + try { + A.minusEquals(S); + errorCount = try_failure(errorCount,"minusEquals conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("minusEquals conformance check... ",""); + } + if (A.minus(Z).norm1() != 0.) { + errorCount = try_failure(errorCount,"minusEquals... ","(difference of identical Matrices is nonzero,\nSubsequent use of minus should be suspect)"); + } else { + try_success("minusEquals... ",""); + } + + A = R.copy(); + B = Matrix.random(A.getRowDimension(),A.getColumnDimension()); + C = A.minus(B); + try { + S = A.plus(S); + errorCount = try_failure(errorCount,"plus conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("plus conformance check... ",""); + } + try { + check(C.plus(B),A); + try_success("plus... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"plus... ","(C = A - B, but C + B != A)"); + } + C = A.minus(B); + C.plusEquals(B); + try { + A.plusEquals(S); + errorCount = try_failure(errorCount,"plusEquals conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("plusEquals conformance check... ",""); + } + try { + check(C,A); + try_success("plusEquals... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"plusEquals... ","(C = A - B, but C = C + B != A)"); + } + A = R.uminus(); + try { + check(A.plus(R),Z); + try_success("uminus... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"uminus... ","(-A + A != zeros)"); + } + A = R.copy(); + O = new Matrix(A.getRowDimension(),A.getColumnDimension(),1.0); + C = A.arrayLeftDivide(R); + try { + S = A.arrayLeftDivide(S); + errorCount = try_failure(errorCount,"arrayLeftDivide conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayLeftDivide conformance check... ",""); + } + try { + check(C,O); + try_success("arrayLeftDivide... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayLeftDivide... ","(M.\\M != ones)"); + } + try { + A.arrayLeftDivideEquals(S); + errorCount = try_failure(errorCount,"arrayLeftDivideEquals conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayLeftDivideEquals conformance check... ",""); + } + A.arrayLeftDivideEquals(R); + try { + check(A,O); + try_success("arrayLeftDivideEquals... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayLeftDivideEquals... ","(M.\\M != ones)"); + } + A = R.copy(); + try { + A.arrayRightDivide(S); + errorCount = try_failure(errorCount,"arrayRightDivide conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayRightDivide conformance check... ",""); + } + C = A.arrayRightDivide(R); + try { + check(C,O); + try_success("arrayRightDivide... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayRightDivide... ","(M./M != ones)"); + } + try { + A.arrayRightDivideEquals(S); + errorCount = try_failure(errorCount,"arrayRightDivideEquals conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayRightDivideEquals conformance check... ",""); + } + A.arrayRightDivideEquals(R); + try { + check(A,O); + try_success("arrayRightDivideEquals... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayRightDivideEquals... ","(M./M != ones)"); + } + A = R.copy(); + B = Matrix.random(A.getRowDimension(),A.getColumnDimension()); + try { + S = A.arrayTimes(S); + errorCount = try_failure(errorCount,"arrayTimes conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayTimes conformance check... ",""); + } + C = A.arrayTimes(B); + try { + check(C.arrayRightDivideEquals(B),A); + try_success("arrayTimes... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayTimes... ","(A = R, C = A.*B, but C./B != A)"); + } + try { + A.arrayTimesEquals(S); + errorCount = try_failure(errorCount,"arrayTimesEquals conformance check... ","nonconformance not raised"); + } catch ( IllegalArgumentException e ) { + try_success("arrayTimesEquals conformance check... ",""); + } + A.arrayTimesEquals(B); + try { + check(A.arrayRightDivideEquals(B),R); + try_success("arrayTimesEquals... ",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"arrayTimesEquals... ","(A = R, A = A.*B, but A./B != R)"); + } + +/** + I/O methods: + read + print + serializable: + writeObject + readObject +**/ + print("\nTesting I/O methods...\n"); + try { + DecimalFormat fmt = new DecimalFormat("0.0000E00"); + fmt.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); + + PrintWriter FILE = new PrintWriter(new FileOutputStream("JamaTestMatrix.out")); + A.print(FILE,fmt,10); + FILE.close(); + R = Matrix.read(new BufferedReader(new FileReader("JamaTestMatrix.out"))); + if (A.minus(R).norm1() < .001 ) { + try_success("print()/read()...",""); + } else { + errorCount = try_failure(errorCount,"print()/read()...","Matrix read from file does not match Matrix printed to file"); + } + } catch ( java.io.IOException ioe ) { + warningCount = try_warning(warningCount,"print()/read()...","unexpected I/O error, unable to run print/read test; check write permission in current directory and retry"); + } catch(Exception e) { + try { + e.printStackTrace(System.out); + warningCount = try_warning(warningCount,"print()/read()...","Formatting error... will try JDK1.1 reformulation..."); + DecimalFormat fmt = new DecimalFormat("0.0000"); + PrintWriter FILE = new PrintWriter(new FileOutputStream("JamaTestMatrix.out")); + A.print(FILE,fmt,10); + FILE.close(); + R = Matrix.read(new BufferedReader(new FileReader("JamaTestMatrix.out"))); + if (A.minus(R).norm1() < .001 ) { + try_success("print()/read()...",""); + } else { + errorCount = try_failure(errorCount,"print()/read() (2nd attempt) ...","Matrix read from file does not match Matrix printed to file"); + } + } catch ( java.io.IOException ioe ) { + warningCount = try_warning(warningCount,"print()/read()...","unexpected I/O error, unable to run print/read test; check write permission in current directory and retry"); + } + } + + R = Matrix.random(A.getRowDimension(),A.getColumnDimension()); + String tmpname = "TMPMATRIX.serial"; + try { + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(tmpname)); + out.writeObject(R); + ObjectInputStream sin = new ObjectInputStream(new FileInputStream(tmpname)); + A = (Matrix) sin.readObject(); + + try { + check(A,R); + try_success("writeObject(Matrix)/readObject(Matrix)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"writeObject(Matrix)/readObject(Matrix)...","Matrix not serialized correctly"); + } + } catch ( java.io.IOException ioe ) { + warningCount = try_warning(warningCount,"writeObject()/readObject()...","unexpected I/O error, unable to run serialization test; check write permission in current directory and retry"); + } catch(Exception e) { + errorCount = try_failure(errorCount,"writeObject(Matrix)/readObject(Matrix)...","unexpected error in serialization test"); + } + +/** + LA methods: + transpose + times + cond + rank + det + trace + norm1 + norm2 + normF + normInf + solve + solveTranspose + inverse + chol + eig + lu + qr + svd +**/ + + print("\nTesting linear algebra methods...\n"); + A = new Matrix(columnwise,3); + T = new Matrix(tvals); + T = A.transpose(); + try { + check(A.transpose(),T); + try_success("transpose...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"transpose()...","transpose unsuccessful"); + } + A.transpose(); + try { + check(A.norm1(),columnsummax); + try_success("norm1...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"norm1()...","incorrect norm calculation"); + } + try { + check(A.normInf(),rowsummax); + try_success("normInf()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"normInf()...","incorrect norm calculation"); + } + try { + check(A.normF(),Math.sqrt(sumofsquares)); + try_success("normF...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"normF()...","incorrect norm calculation"); + } + try { + check(A.trace(),sumofdiagonals); + try_success("trace()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"trace()...","incorrect trace calculation"); + } + try { + check(A.getMatrix(0,A.getRowDimension()-1,0,A.getRowDimension()-1).det(),0.); + try_success("det()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"det()...","incorrect determinant calculation"); + } + SQ = new Matrix(square); + try { + check(A.times(A.transpose()),SQ); + try_success("times(Matrix)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"times(Matrix)...","incorrect Matrix-Matrix product calculation"); + } + try { + check(A.times(0.),Z); + try_success("times(double)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"times(double)...","incorrect Matrix-scalar product calculation"); + } + + A = new Matrix(columnwise,4); + QRDecomposition QR = A.qr(); + R = QR.getR(); + try { + check(A,QR.getQ().times(R)); + try_success("QRDecomposition...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"QRDecomposition...","incorrect QR decomposition calculation"); + } + SingularValueDecomposition SVD = A.svd(); + try { + check(A,SVD.getU().times(SVD.getS().times(SVD.getV().transpose()))); + try_success("SingularValueDecomposition...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"SingularValueDecomposition...","incorrect singular value decomposition calculation"); + } + DEF = new Matrix(rankdef); + try { + check(DEF.rank(),Math.min(DEF.getRowDimension(),DEF.getColumnDimension())-1); + try_success("rank()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"rank()...","incorrect rank calculation"); + } + B = new Matrix(condmat); + SVD = B.svd(); + double [] singularvalues = SVD.getSingularValues(); + try { + check(B.cond(),singularvalues[0]/singularvalues[Math.min(B.getRowDimension(),B.getColumnDimension())-1]); + try_success("cond()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"cond()...","incorrect condition number calculation"); + } + int n = A.getColumnDimension(); + A = A.getMatrix(0,n-1,0,n-1); + A.set(0,0,0.); + LUDecomposition LU = A.lu(); + try { + check(A.getMatrix(LU.getPivot(),0,n-1),LU.getL().times(LU.getU())); + try_success("LUDecomposition...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"LUDecomposition...","incorrect LU decomposition calculation"); + } + X = A.inverse(); + try { + check(A.times(X),Matrix.identity(3,3)); + try_success("inverse()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"inverse()...","incorrect inverse calculation"); + } + O = new Matrix(SUB.getRowDimension(),1,1.0); + SOL = new Matrix(sqSolution); + SQ = SUB.getMatrix(0,SUB.getRowDimension()-1,0,SUB.getRowDimension()-1); + try { + check(SQ.solve(SOL),O); + try_success("solve()...",""); + } catch ( java.lang.IllegalArgumentException e1 ) { + errorCount = try_failure(errorCount,"solve()...",e1.getMessage()); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"solve()...",e.getMessage()); + } + A = new Matrix(pvals); + CholeskyDecomposition Chol = A.chol(); + Matrix L = Chol.getL(); + try { + check(A,L.times(L.transpose())); + try_success("CholeskyDecomposition...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"CholeskyDecomposition...","incorrect Cholesky decomposition calculation"); + } + X = Chol.solve(Matrix.identity(3,3)); + try { + check(A.times(X),Matrix.identity(3,3)); + try_success("CholeskyDecomposition solve()...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"CholeskyDecomposition solve()...","incorrect Choleskydecomposition solve calculation"); + } + EigenvalueDecomposition Eig = A.eig(); + Matrix D = Eig.getD(); + Matrix V = Eig.getV(); + try { + check(A.times(V),V.times(D)); + try_success("EigenvalueDecomposition (symmetric)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"EigenvalueDecomposition (symmetric)...","incorrect symmetric Eigenvalue decomposition calculation"); + } + A = new Matrix(evals); + Eig = A.eig(); + D = Eig.getD(); + V = Eig.getV(); + try { + check(A.times(V),V.times(D)); + try_success("EigenvalueDecomposition (nonsymmetric)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"EigenvalueDecomposition (nonsymmetric)...","incorrect nonsymmetric Eigenvalue decomposition calculation"); + } + + try { + print("\nTesting Eigenvalue; If this hangs, we've failed\n"); + Matrix bA = new Matrix(badeigs); + EigenvalueDecomposition bEig = bA.eig(); + try_success("EigenvalueDecomposition (hang)...",""); + } catch ( java.lang.RuntimeException e ) { + errorCount = try_failure(errorCount,"EigenvalueDecomposition (hang)...", + "incorrect termination"); + } + + + print("\nTestMatrix completed.\n"); + print("Total errors reported: " + Integer.toString(errorCount) + "\n"); + print("Total warnings reported: " + Integer.toString(warningCount) + "\n"); + } + + /** private utility routines **/ + + /** Check magnitude of difference of scalars. **/ + + private static void check(double x, double y) { + double eps = Math.pow(2.0,-52.0); + if (x == 0 & Math.abs(y) < 10*eps) return; + if (y == 0 & Math.abs(x) < 10*eps) return; + if (Math.abs(x-y) > 10*eps*Math.max(Math.abs(x),Math.abs(y))) { + throw new RuntimeException("The difference x-y is too large: x = " + Double.toString(x) + " y = " + Double.toString(y)); + } + } + + /** Check norm of difference of "vectors". **/ + + private static void check(double[] x, double[] y) { + if (x.length == y.length ) { + for (int i=0;i 1000*eps*Math.max(X.norm1(),Y.norm1())) { + throw new RuntimeException("The norm of (X-Y) is too large: " + Double.toString(X.minus(Y).norm1())); + } + } + + /** Shorten spelling of print. **/ + + private static void print (String s) { + System.out.print(s); + } + + /** Print appropriate messages for successful outcome try **/ + + private static void try_success (String s,String e) { + print("> " + s + "success\n"); + if ( e != "" ) { + print("> Message: " + e + "\n"); + } + } + /** Print appropriate messages for unsuccessful outcome try **/ + + private static int try_failure (int count, String s,String e) { + print("> " + s + "*** failure ***\n> Message: " + e + "\n"); + return ++count; + } + + /** Print appropriate messages for unsuccessful outcome try **/ + + private static int try_warning (int count, String s,String e) { + print("> " + s + "*** warning ***\n> Message: " + e + "\n"); + return ++count; + } + + /** Print a row vector. **/ + + private static void print(double[] x, int w, int d) { + // Use format Fw.d for all elements. + System.out.print("\n"); + new Matrix(x,1).print(w,d); + print("\n"); + } + +} diff --git a/Java/src/Jama/util/Maths.java b/Java/src/Jama/util/Maths.java new file mode 100644 index 00000000..087a6a74 --- /dev/null +++ b/Java/src/Jama/util/Maths.java @@ -0,0 +1,20 @@ +package Jama.util; + +public class Maths { + + /** sqrt(a^2 + b^2) without under/overflow. **/ + + public static double hypot(double a, double b) { + double r; + if (Math.abs(a) > Math.abs(b)) { + r = b/a; + r = Math.abs(a)*Math.sqrt(1+r*r); + } else if (b != 0) { + r = a/b; + r = Math.abs(b)*Math.sqrt(1+r*r); + } else { + r = 0.0; + } + return r; + } +} diff --git a/Java/src/Viewer2D/EdgesFrame.java b/Java/src/Viewer2D/EdgesFrame.java index b73c609c..0cde000c 100755 --- a/Java/src/Viewer2D/EdgesFrame.java +++ b/Java/src/Viewer2D/EdgesFrame.java @@ -21,22 +21,32 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.awt.event.KeyListener; +import java.awt.event.ComponentListener; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.InputEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; +import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JColorChooser; +import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; @@ -44,10 +54,15 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollPane; import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.KeyStroke; + +import Viewer2D.EdgesPanel.modes; /** * SESS - 2014.05.11: @@ -66,7 +81,7 @@ // TODO: SESS - Also show "working, please wait..." // TODO: SESS - When edges panel should repaint is repainting this frame // should be only the edges panel -public class EdgesFrame extends JFrame { +public class EdgesFrame extends JFrame implements KeyListener,ComponentListener { private static final long serialVersionUID = 1099866981814538683L; static final int FRAME_WIDTH = 1024; @@ -75,6 +90,10 @@ public class EdgesFrame extends JFrame { private Edge[] edges; private Vertex[] vertices; + private HashMap labels; + + private double labelScale; + private EdgesPanel panel; // Has the edges drawn private JTextField statusBar; // Shows highlighted/user info @@ -119,6 +138,11 @@ public class EdgesFrame extends JFrame { private Color fontColor, vertexColor, edgeColor, backgroundColor; private int STATUSBAR_Y; + + + JToggleButton hand; + + JToggleButton magnifier; public EdgesFrame(String title, int x, int y) { super(title); @@ -128,7 +152,7 @@ public EdgesFrame(String title, int x, int y) { STATUSBAR_Y = 50; moveStepSize = .2; - zoomStepSize = .2; + zoomStepSize = .8; windowSizes = new int[2]; windowSizes[0] = x; @@ -147,12 +171,16 @@ public EdgesFrame(String title, int x, int y) { container.setLayout(layout); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - panel = new EdgesPanel(edges, vertices, windowSizes[0], windowSizes[1]); + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + panel = new EdgesPanel(edges, vertices, new HashMap(),screen.width,screen.height);//windowSizes[0], windowSizes[1]); panel.setBackground(backgroundColor); setMenuBars(); + hand = new JToggleButton("Hand"); + magnifier = new JToggleButton("Magnifier"); setButtons(); + + addKeyListener(this); JScrollPane scrollPane = new JScrollPane(panel); container.add(scrollPane, BorderLayout.CENTER); @@ -163,13 +191,85 @@ public EdgesFrame(String title, int x, int y) { panel.setStatusBar(statusBar); // Let's start with the screen size - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + int width = Math.min(screen.width, FRAME_WIDTH); int height = Math.min(screen.height, FRAME_HEIGHT); setSize(width, height); setLocation((screen.width - width) / 2, (screen.height - height) / 2); setResizable(true); setVisible(true); + labels = new HashMap<>(); + labelScale = 1; + addComponentListener(this); + /*ActionListener okListener = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("2"); + } + }; + getRootPane().registerKeyboardAction(okListener, + KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), + JComponent.WHEN_IN_FOCUSED_WINDOW);*/ + //KeyStroke k = .getKeyStroke(' ' ); + //k.getKeyStroke(keyChar, onKeyRelease) + + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(' ' ), "SPACE"); + + panel.getActionMap().put("SPACE", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + //do nothing + + if (panel.getMode()!=modes.handmode) + { + setMode(modes.handmode); + } + else + setMode(modes.nomode); + } + }); + + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke( KeyEvent.VK_ADD, InputEvent.CTRL_MASK ), "ZOOMIN"); + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK),"ZOOMIN"); + + + panel.getActionMap().put("ZOOMIN", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + Point p = panel.getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + + panel.zoom2Point(x1, y1); + panel.setPaintImage(); + panel.repaint(); + } + }); + + + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, InputEvent.CTRL_MASK ), "ZOOMOUT"); + + //panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK),"ZOOMOUT"); + + panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_DOWN_MASK),"ZOOMOUT"); + + + panel.getActionMap().put("ZOOMOUT", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + Point p = panel.getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + + panel.zoomOutFromPoint(x1, y1); + panel.setPaintImage(); + panel.repaint(); + } + }); + + } private JFileChooser fileChooser; @@ -210,9 +310,11 @@ private void setMenuBars() { JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); - // LOAD THE EDGES FILE ( SOMETHING.lgl ) - JMenuItem openFile = new JMenuItem("Open .lgl file"); - openFile.setMnemonic('O'); + + // load all + + JMenuItem openFile = new JMenuItem("Load all files"); + openFile.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JFileChooser chooser = getFileChooser("lgl"); @@ -220,11 +322,38 @@ public void actionPerformed(ActionEvent e) { if (returnVal == JFileChooser.APPROVE_OPTION) { edgesFile = chooser.getSelectedFile(); loadSHORTFile(edgesFile); + File coordsFile = new File(edgesFile.getPath().replace(".lgl", ".coords")); + if (coordsFile.isFile()) + loadCoordsFile(coordsFile); + File colorsFile = new File(edgesFile.getPath().replace(".lgl", ".colors")); + + if (colorsFile.isFile()) + loadEdgeColorFile(colorsFile); + + File labelsFile = new File(edgesFile.getPath().replace(".lgl", ".labels")); + + if (labelsFile.isFile()) + loadLabelFile(labelsFile); } } }); fileMenu.add(openFile); + // LOAD THE EDGES FILE ( SOMETHING.lgl ) + JMenuItem openAllFile = new JMenuItem("Open .lgl file"); + openAllFile.setMnemonic('O'); + openAllFile.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = getFileChooser("lgl"); + int returnVal = chooser.showOpenDialog(EdgesFrame.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + edgesFile = chooser.getSelectedFile(); + loadSHORTFile(edgesFile); + } + } + }); + fileMenu.add(openAllFile); + // LOAD THE 2D COORDS JMenuItem cFile = new JMenuItem("Open 2D Coords file"); cFile.setMnemonic('C'); @@ -335,6 +464,27 @@ public void actionPerformed(ActionEvent e) { }); fileMenu.add(reload); + // load labels + + JMenuItem labelMenu = new JMenuItem("Load labels"); + labelMenu.setMnemonic('L'); + labelMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (vertexCheck()) { + JFileChooser chooser = getFileChooser(); + int returnVal = chooser.showOpenDialog(EdgesFrame.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + loadLabelFile(chooser.getSelectedFile()); + } + } + } + }); + fileMenu.add(labelMenu); + + + + + // APPLICATION EXIT JMenuItem exit = new JMenuItem("Exit"); exit.setMnemonic('x'); @@ -391,13 +541,42 @@ public void actionPerformed(ActionEvent e) { "Error", JOptionPane.ERROR_MESSAGE); } else { zoomStepSize = newZoomStepSize; - panel.setZoomStepSize(zoomStepSize); + formatter.setLabelScale(newZoomStepSize); + + } } } }); edit.add(zoomsize); + // change label scale + + JMenuItem labelscalesize = new JMenuItem("Change scale for Labels"); + + labelscalesize.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String newSize = JOptionPane.showInputDialog(EdgesFrame.this, + "Enter the new label scale", + Double.toString(labelScale)); + if (newSize != null) { + double newLabelScale = Double.parseDouble(newSize); + if (newLabelScale <= 0 ) { + JOptionPane.showMessageDialog(null, "Illegal Value", + "Error", JOptionPane.ERROR_MESSAGE); + } else { + labelScale = newLabelScale; + + formatter.setLabelScale(labelScale); + panel.setPaintImage(); // TODO: move inside panel.xxxx()? + panel.repaint(); + } + } + } + }); + edit.add(labelscalesize); + + // BUTTON TO REMOVE TRANSIENT EDGES blockers = new JRadioButton("Remove Transient Edges"); blockers.setMnemonic('R'); @@ -433,6 +612,8 @@ public void actionPerformed(ActionEvent e) { if (zoomHighlighted.isSelected()) { panel.prepZoomPoint(true); statusMessage = "Enabled Zoom Point"; + setMode(modes.nomode); + } else { panel.prepZoomPoint(false); statusMessage = "Disabled Zoom Point"; @@ -451,6 +632,7 @@ public void actionPerformed(ActionEvent e) { if (zoomRegion.isSelected()) { panel.prepZoomRegion(true); statusMessage = "Enabled Zoom Region"; + setMode(modes.nomode); } else { panel.prepZoomRegion(false); statusMessage = "Disabled Zoom Region"; @@ -802,6 +984,23 @@ public void loadVertexColorFile(File f) { } } + public void loadLabelFile(File f) { + System.out.println("Loading Label File " + f.getAbsolutePath()); + try { + edgesio.loadLabelFile(f); + // Update the panel with tlabels + panel.addLabels(edgesio.getLabels()); + panel.setPaintImage(); + panel.repaint(); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(null, "File Not Found", "Error", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(null, "IO Error: Check File Format", + "Error", JOptionPane.ERROR_MESSAGE); + } + } + public void loadCoordsFile(File f) { coordsFile = f; try { @@ -823,7 +1022,7 @@ public void loadCoordsFile(File f) { panel.setFont(font); panel.setMoveStepSize(moveStepSize); panel.setZoomStepSize(zoomStepSize); - formatter = new FormatVertex(vertices, edgesio.getStats(), + formatter = new FormatVertex(vertices, edgesio.getLabels(),labelScale,0,0,0,0,false,edgesio.getStats(), windowSizes, threads); panel.setFormatter(formatter); panel.fitData(); @@ -854,22 +1053,70 @@ public void loadCoordsFile(File f) { */ private JPanel buttonsPanel; + private void resetZooms() + { + panel.prepZoomPoint(false); + panel.prepZoomRegion(false); + zoomHighlighted.setSelected(false); + zoomRegion.setSelected(false); + } + + private void inactiveButton(JToggleButton b) + { + //b.setContentAreaFilled(true); + b.setForeground(new JButton().getForeground()); + b.setSelected(false); + + } + + private void setMode(modes mode) + { + if (mode==modes.handmode) + { + panel.handMode(); + resetZooms(); + magnifier.setSelected(false); + inactiveButton(magnifier); + //hand.setContentAreaFilled(false); + //hand.setOpaque(true); + hand.setForeground(Color.green); + + } + + + + if (mode==modes.magnifiermode) + { + panel.magnifierMode(); + resetZooms(); + hand.setSelected(false); + inactiveButton(hand); + //magnifier.setContentAreaFilled(false); + //magnifier.setOpaque(true); + magnifier.setForeground(Color.green); + } + + if (mode==modes.nomode) + { + inactiveButton(hand); + inactiveButton(magnifier); + panel.noMode(); + + } + + + } + + + private void setButtons() { buttonsPanel = new JPanel(); ((FlowLayout) buttonsPanel.getLayout()).setAlignment(FlowLayout.LEFT); container.add(buttonsPanel, BorderLayout.NORTH); - JButton undo = new JButton("Undo"); - undo.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - panel.undo(); - panel.setPaintImage(); // TODO: move inside panel.xxxx()? - panel.repaint(); - } - }); - buttonsPanel.add(undo); + - JButton zoomIn = new JButton("In"); + /*JButton zoomIn = new JButton("In"); zoomIn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { VertexFitter f = new VertexFitter(); @@ -891,9 +1138,61 @@ public void actionPerformed(ActionEvent e) { panel.repaint(); } }); - buttonsPanel.add(zoomOut); + buttonsPanel.add(zoomOut);*/ + + + + //hand.setContentAreaFilled(false); + //hand.setOpaque(true); + //hand.setBackground(Color.black); + hand.setFocusable(false); + + //hand.setContentAreaFilled(false); + hand.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println(hand.getModel().isSelected()); + if (hand.getModel().isSelected()) + { + setMode(modes.handmode); + } + else + { + setMode(modes.nomode); + + //hand.setSelected(false); + } + + } + }); + buttonsPanel.add(hand); + + + + //magnifier.setContentAreaFilled(false); + //magnifier.setOpaque(true); + //magnifier.setBackground(Color.black); + magnifier.setFocusable(false); + + + //magnifier.setContentAreaFilled(false); + magnifier.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println(magnifier.getModel().isSelected()); + if (magnifier.getModel().isSelected()) + { + setMode(modes.magnifiermode); + } + else + { + setMode(modes.nomode); + //hand.setSelected(false); + } + + } + }); + buttonsPanel.add(magnifier); - JButton moveup = new JButton("Up"); + /*JButton moveup = new JButton("Up"); moveup.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { panel.moveUp(); @@ -931,14 +1230,14 @@ public void actionPerformed(ActionEvent e) { panel.repaint(); } }); - buttonsPanel.add(moveright); + buttonsPanel.add(moveright);*/ JButton fit = new JButton("Fit"); fit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // TODO: SESS - twice? panel.fitData(); - panel.fitData(); + //panel.fitData(); System.out.println("Fit data still buggy."); panel.setPaintImage(); // TODO: move inside panel.xxxx()? panel.repaint(); @@ -996,8 +1295,35 @@ public void actionPerformed(ActionEvent e) { panel.showVertices(false); showVertices.setSelected(false); buttonsPanel.add(showVertices); + + JButton undo = new JButton("Undo"); + undo.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + panel.undo(); + panel.setPaintImage(); // TODO: move inside panel.xxxx()? + panel.repaint(); + } + }); + buttonsPanel.add(undo); } + public void keyTyped(KeyEvent e) { + // Sys(e, "KEY TYPED: "); + } + + /** Handle the key-pressed event from the text field. */ + public void keyPressed(KeyEvent e) { + System.out.println(e.getKeyChar()); + if (e.getKeyChar()==' ') + System.out.println("yes"); + // displayInfo(e, "KEY PRESSED: "); + } + + /** Handle the key-released event from the text field. */ + public void keyReleased(KeyEvent e) { + // displayInfo(e, "KEY RELEASED: "); + } + class StyleHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { int style = 0; @@ -1037,6 +1363,15 @@ private boolean vertexCheck() { return true; } + public void componentHidden(ComponentEvent e) {} + public void componentMoved(ComponentEvent e) {} + public void componentShown(ComponentEvent e) {} + + public void componentResized(ComponentEvent e) { + Dimension newSize = e.getComponent().getBounds().getSize(); + System.out.println("size:"+newSize.width+" "+newSize.height); + } + } // //////////////////////////////////////////////////////////////// @@ -1095,6 +1430,8 @@ public String getExtension(File f) { return ext; } + + } // //////////////////////////////////////////////////////////////// diff --git a/Java/src/Viewer2D/EdgesPanel.java b/Java/src/Viewer2D/EdgesPanel.java index 404c3c20..a82bce42 100755 --- a/Java/src/Viewer2D/EdgesPanel.java +++ b/Java/src/Viewer2D/EdgesPanel.java @@ -20,20 +20,31 @@ package Viewer2D; import java.awt.Color; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelListener; + +import java.awt.event.MouseWheelEvent; + import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; +import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.text.MessageFormat; @@ -42,12 +53,21 @@ import java.util.Map; import javax.imageio.ImageIO; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JTextField; import javax.swing.SwingUtilities; import Jama.Matrix; +import java.awt.BasicStroke; +import Viewer2D.Label; +import java.awt.AlphaComposite; +import java.awt.FontFormatException; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.Image; /** * SESS - 2014.05.13: @@ -58,12 +78,14 @@ * */ public class EdgesPanel extends JPanel implements MouseListener, - MouseMotionListener { + MouseMotionListener, MouseWheelListener { private static final long serialVersionUID = -765273216020721560L; private Edge[] edges; private Vertex[] vertices; + private HashMap labels; + private int xWindowSize, yWindowSize; private VertexFitter fitter; @@ -75,6 +97,8 @@ public class EdgesPanel extends JPanel implements MouseListener, private Font font; + private HashMap fontMap; + // These are hashes relating edge/vertex id // to specific info private HashMap edgeColorMap; @@ -103,6 +127,20 @@ public class EdgesPanel extends JPanel implements MouseListener, // Default font color private Color fontColor; + enum modes + { + nomode, + handmode, + magnifiermode + + + } + + private modes mode; + private boolean handtouch = false; + + private Cursor cursorMagnifier; + private double x1, y1, x2, y2; private Matrix mins, maxs; private Matrix inverted; @@ -110,6 +148,12 @@ public class EdgesPanel extends JPanel implements MouseListener, private BufferedImage bufferedImage; private boolean paintImage; + private double defaultScale; + + ModeChanged modeChange; + + private JPopupMenu menu; + protected BufferedImage getBufferedImage() { if (bufferedImage == null) { bufferedImage = new BufferedImage(xWindowSize, yWindowSize, @@ -123,6 +167,12 @@ protected BufferedImage getBufferedImage() { // this.bufferedImage = bufferedImage; // } + public interface ModeChanged { + //declare abstract method + void onChange(modes m); + } + + public boolean isPaintImage() { return paintImage; } @@ -135,7 +185,7 @@ public void setPaintImage() { paintImage = true; } - public EdgesPanel(Edge[] edges, Vertex[] vertices, int xWindowSize, + public EdgesPanel(Edge[] edges, Vertex[] vertices, HashMap labels, int xWindowSize, int yWindowSize) { super(); @@ -145,9 +195,12 @@ public EdgesPanel(Edge[] edges, Vertex[] vertices, int xWindowSize, this.setPreferredSize(new Dimension(xWindowSize, yWindowSize)); this.edges = edges; this.vertices = vertices; + this.labels = labels; addMouseListener(this); addMouseMotionListener(this); + addMouseWheelListener(this); + // By default don't draw ids idsIncluded = false; @@ -162,6 +215,7 @@ public EdgesPanel(Edge[] edges, Vertex[] vertices, int xWindowSize, // These are the default colors edgeColorMap = new HashMap(); vertexColorMap = new HashMap(); + fontMap = new HashMap(); edgeColor = Color.black; fontColor = Color.blue; // Lets start with transparent @@ -173,6 +227,138 @@ public EdgesPanel(Edge[] edges, Vertex[] vertices, int xWindowSize, // Matrices need for zooming moving etc mins = new Matrix(2, 1); maxs = new Matrix(2, 1); + mode = modes.nomode; + Image image = null; + Toolkit toolkit = Toolkit.getDefaultToolkit(); + String sep = System.getProperty("file.separator"); + String workingdir = System.getProperty("user.dir"); + //System.out.println("Working Directory = " + System.getProperty("user.dir")); + + java.net.URL imgURL = getClass().getResource("icons8-magnifier-67_32x32.png"); + if(null != imgURL) { + System.out.println("Icon URL = " + imgURL.getFile()); + //Image image = toolkit.getImage(workingdir+sep+"Java/src/Viewer2D"+sep+"icons8-magnifier-67_32x32.png"); + image = toolkit.getImage(imgURL); + } + cursorMagnifier = null; // toolkit.createCustomCursor(image , new Point(0,0), "magnifier"); + setPopup() ; + defaultScale = 1; + } + + + + private void setPopup() + { + + menu = new JPopupMenu(); + + + + JMenuItem fos = new JMenuItem("Fit on Screen"); + JMenuItem n100 = new JMenuItem("100%"); + JMenuItem n200 = new JMenuItem("200%"); + JMenuItem zoomIn = new JMenuItem("Zoom In"); + JMenuItem zoomOut = new JMenuItem("Zoom Out"); + menu.add(fos); + menu.add(n100); + menu.add(n200); + menu.addSeparator(); + menu.add(zoomIn); + menu.add(zoomOut); + + fos.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Dimension r = getBounds().getSize(); + xWindowSize = r.width; + yWindowSize = r.height; + //bufferedImage = null; + //getBufferedImage(); + formatter.setWindowSizeX(xWindowSize); + formatter.setWindowSizeY(yWindowSize); + fitData(); + + setPaintImage(); // TODO: move inside panel.xxxx()? + repaint(); + } + }); + + n100.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + double scale = formatter.getScale(); + if (scale!=defaultScale) + { + Point p = getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + double targetScale; + + targetScale = defaultScale/scale; + + zoom2PointBy(x1, y1,targetScale); + setPaintImage(); + repaint(); + + } + + } + }); + + n200.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + double scale = formatter.getScale(); + if (scale!=defaultScale*2) + { + Point p = getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + double targetScale; + + targetScale = defaultScale*2/scale; + + zoom2PointBy(x1, y1,targetScale); + setPaintImage(); + repaint(); + + } + + } + }); + + zoomIn.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + + Point p = getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + + + zoom2Point(x1, y1); + setPaintImage(); + repaint(); + + } + }); + + zoomOut.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Point p = getMousePosition(); + int x1 = (int)p.getX(); + int y1 = (int)p.getY(); + + + zoomOutFromPoint(x1, y1); + setPaintImage(); + repaint(); + + } + }); + + + //setComponentPopupMenu(menu); + + + //panel.re + } // ------------------------------------------------------- @@ -228,6 +414,7 @@ private void paintImage(Graphics2D g2){ paintColoredEdges(g2); paintVertices(g2); paintIds(g2); + paintLabels(g2); } } @@ -322,6 +509,7 @@ public void paintColoredVertices(Graphics g) { } } + public void paintComponent(Graphics g) { System.out.println("paintComponent() " + g); super.paintComponent(g); @@ -342,9 +530,12 @@ public void paintComponent(Graphics g) { } public void applyFit(VertexFitter f) { - for (int ii = 0; ii < vertices.length; ++ii) { + /*for (int ii = 0; ii < vertices.length; ++ii) { f.fitVertex(vertices[ii]); - } + + }*/ + formatter.getFitter().setManipulationMatrix(f.getManipulationMatrix()); + formatter.applyTransformation(); fitter = f; inverted = fitter.getManipulationMatrix().inverse(); fitter.setManipulationMatrix(inverted); @@ -352,6 +543,7 @@ public void applyFit(VertexFitter f) { public void fitData() { formatter.fitDataToWindow(); + defaultScale = formatter.getScale(); } public void setEdges(Edge[] e) { @@ -376,6 +568,10 @@ public void addVertexColors(HashMap h) { vertexColorMap.putAll(h); } + public void addLabels(HashMap h) { + labels.putAll(h); + } + public void clearAllEdgeColors() { edgeColorMap.clear(); } @@ -671,6 +867,155 @@ private void paintIds(Graphics2D g) { } } + private class Fontid + { + String filename; + float size; + + Fontid(String f, float s) + { + filename = f; + size = s; + + } + public boolean equals(Object o) { + return ((Fontid)o).filename == this.filename && ((Fontid)o).size == this.size ; + } + public int hashCode() { + return filename.hashCode()+(int)(size*10); + } + + } + + private Font getFont(String ttffile, float size) + { + Font f = font; + Fontid id = new Fontid(ttffile,size); + if (fontMap.containsKey(id)) + { + return fontMap.get(id); + } + + try { + f = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(ttffile)).deriveFont(size); + fontMap.put(id,f); + } catch (FontFormatException ffe) { + System.out.println("Tried to load a bad font..."); + ffe.printStackTrace(); + } + catch (FileNotFoundException e) { + System.out.println("could not open file"+ ttffile); + e.printStackTrace(); + } + catch (IOException ioe) { + System.out.println("I have no idea what went wrong"); + ioe.printStackTrace(); + } + + return f; + } + + private void drawStringWithBackground(Graphics2D g,String text, int x, int y, Color bgcolor, Color textcolor) + { + + //g.setFont(new Font()) + //Font.createFont(Font.TRUETYPE_FONT, new FileInputStream("font.ttf")); + FontMetrics fm = g.getFontMetrics(); + Rectangle2D rect = fm.getStringBounds(text, g); + + if (bgcolor!=null) + { + g.setColor(bgcolor); + g.fillRect(x, + y - fm.getAscent(), + (int) rect.getWidth(), + (int) rect.getHeight()); + } + + g.setColor(textcolor); + g.drawString(text, x, y); + + + } + + public void paintLabels(Graphics2D g) + { + if (vertices == null) { + return; + } + if (labels == null) { + return; + } + labels.forEach((k,v) -> { //System.out.println("Key = " + //+ k + ", Value = " + v); + //System.out.println("key: " + name); + //Label l = labels.get(name); + Label l = (Label)v; + Vertex vertex = (Vertex) k; + if (l.linesize!=0 && l.linecolor != null) + { + g.setColor(l.linecolor); + g.setStroke(new BasicStroke(l.linesize*1.0f)); + } + double rad = (90-l.lineangle)/360*2*Math.PI; + double xdiff = l.linelength * Math.cos(rad); + double ydiff = -l.linelength * Math.sin(rad); + double xstart = vertex.location().get(0, 0); + double ystart = vertex.location().get(1, 0); + double xend = xstart+xdiff; + double yend = ystart+ydiff; + if (l.linesize!=0 && l.linecolor != null) + g.draw(new Line2D.Double(xstart,ystart,xend, yend)); + + if (l.shape.equals("circle")) + { + double radius = l.shapesize; + //g.setColor(l.shapefillcolor); + //g.fillOval(x, y, width, height); + //Shape theCircle = new Ellipse2D.Double(xstart - radius, ystart - radius, 2.0 * radius, 2.0 * radius); + + //BasicStroke dashed =new BasicStroke(3.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,10.0f); + BasicStroke stroke1 =new BasicStroke(l.shapeborderwidth); + Ellipse2D.Double circle = new Ellipse2D.Double(xstart - radius, ystart - radius, 2.0 * radius, 2.0 * radius); + Ellipse2D.Double circleBorder = new Ellipse2D.Double(xstart - radius, ystart - radius, 2.0 * radius, 2.0 * radius); + g.setColor(l.shapefillcolor); + //g2.setRenderingHints(hints); + g.fill(circle); + //Composite oldComposite=g.getComposite(); + //g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0)); + g.setColor(l.shapebordercolor); + g.setStroke(stroke1); + g.draw(circleBorder); + //g2.setComposite(oldComposite); + } + if (!l.toptext.isEmpty()) + { + g.setFont(getFont(l.toptextttf,(float)l.toptextsize)); + drawStringWithBackground(g,l.toptext, (int)xend, (int)yend, l.topbgfillcolor, l.toptextcolor); + } + if (!l.bottomtext.isEmpty()) + { + double xend2 = xend; + double yend2 = yend; + if (!l.toptext.isEmpty()) + { + FontMetrics fm = g.getFontMetrics(); + Rectangle2D rect = fm.getStringBounds(l.bottomtext, g); + yend2+=rect.getHeight(); + } + else + { + + } + g.setFont(getFont(l.bottomtextttf,(float)l.bottomtextsize)); + drawStringWithBackground(g,l.bottomtext, (int)xend, (int)yend2, l.bottombgfillcolor, l.bottomtextcolor); + } + + + }); + + } + public void paintVertices(Graphics g) { if (vertices == null) { return; @@ -898,6 +1243,7 @@ public void runZoomRegion() { } } + public void zoomIn(VertexFitter f) { if (edges == null) { return; @@ -910,11 +1256,36 @@ public void zoomIn(VertexFitter f) { setPaintImage(); } + public void zoomIn2(VertexFitter f) { + zoom(f,1/zoomStepSize); + } + + public void zoom(VertexFitter f, double scale) + { + if (edges == null) { + return; + } + Transformer trans = new Transformer(); + trans.scale(scale); + f.addManipulation(trans); + setPaintImage(); + + } + + public void zoomOut2(VertexFitter f) { + zoom(f,zoomStepSize); + } + public void zoomOut(VertexFitter f) { if (edges == null) { return; } + /*mins.set(0, 0, -zoomStepSize * xWindowSize); + mins.set(1, 0, -zoomStepSize * yWindowSize); + maxs.set(0, 0, xWindowSize + zoomStepSize * xWindowSize); + maxs.set(1, 0, yWindowSize + zoomStepSize * yWindowSize);*/ + mins.set(0, 0, -zoomStepSize * xWindowSize); mins.set(1, 0, -zoomStepSize * yWindowSize); maxs.set(0, 0, xWindowSize + zoomStepSize * xWindowSize); @@ -923,23 +1294,82 @@ public void zoomOut(VertexFitter f) { zoomPrep(f); } + public void handMode() + { + mode = modes.handmode; + setCursor(new Cursor(Cursor.HAND_CURSOR)); + setComponentPopupMenu(null); + //menu.setVisible(false); + + } + + modes getMode() + { + return mode; + + } + + public void magnifierMode() + { + mode = modes.magnifiermode; + setCursor(cursorMagnifier); + setComponentPopupMenu(menu); + + + } + + public void noMode() + { + mode = modes.nomode; + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + setComponentPopupMenu(null); + //menu.setVisible(false); + } + public void zoom2Point(double x, double y) { if (edges == null) { return; } VertexFitter f = new VertexFitter(); + move2Point(-x, -y, f); + zoomIn2(f); move2Point(x, y, f); - zoomIn(f); + + + //move2Point(-x, -y, f); + //move2Point(-2*x/zoomStepSize, -2*y/zoomStepSize, f); applyFit(f); } + public void zoom2PointBy(double x, double y, double scale) { + if (edges == null) { + return; + } + VertexFitter f = new VertexFitter(); + move2Point(-x, -y, f); + zoom(f,scale); + move2Point(x, y, f); + + + //move2Point(-x, -y, f); + //move2Point(-2*x/zoomStepSize, -2*y/zoomStepSize, f); + applyFit(f); + } + + + public void zoomOutFromPoint(double x, double y) { if (edges == null) { return; } VertexFitter f = new VertexFitter(); + //move2Point(x, y, f); + //zoomOut(f); + move2Point(-x, -y, f); + zoomOut2(f); move2Point(x, y, f); - zoomOut(f); + + applyFit(f); } @@ -949,7 +1379,7 @@ private void zoomPrep(VertexFitter f) { double spany = maxs.get(1, 0) - mins.get(1, 0); // Scale to fit new range scales[0] = xWindowSize / spanx; - scales[1] = yWindowSize / spany; + scales[1] = xWindowSize / spanx;//yWindowSize / spany; Transformer trans = new Transformer(); trans.scale(scales); f.addManipulation(trans); @@ -1045,8 +1475,8 @@ private void move2Point(double x, double y, VertexFitter f) { return; } double[] moves = new double[2]; - moves[0] = -x + xWindowSize * .5; - moves[1] = -y + yWindowSize * .5; + moves[0] = -x;// + xWindowSize * .5; + moves[1] = -y; //+ yWindowSize * .5; Transformer trans = new Transformer(); trans.move(moves); f.addManipulation(trans); @@ -1082,6 +1512,14 @@ private void setRenderingHints(Graphics2D g2) { // ----------------------------------------------------- public void mousePressed(MouseEvent e) { + if (mode == modes.handmode) + { + x1 = e.getX(); + y1 = e.getY(); + handtouch = true; + System.out.println("click"); + } + else if (zoomRegion || idRegion) { x1 = e.getX(); y1 = e.getY(); @@ -1091,6 +1529,10 @@ public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { + if (mode == modes.handmode && handtouch) + { + handtouch = false; + } if (zoomRegion || idRegion) { x2 = e.getX(); y2 = e.getY(); @@ -1113,6 +1555,44 @@ public void mouseReleased(MouseEvent e) { } public void mouseDragged(MouseEvent e) { + if (mode == modes.handmode && handtouch) + { + if (edges == null) { + return; + } + System.out.println("updating" + e.getX()); + double x2 = e.getX(); + double y2 = e.getY(); + + + double ymove = y2-y1; + + double xmove = x2-x1; + + mins.set(0, 0, xmove); + mins.set(1, 0, ymove); + maxs.set(0, 0, xmove+ xWindowSize); + maxs.set(1, 0, ymove + yWindowSize); + + VertexFitter f = new VertexFitter(); + movePrep(f); + applyFit(f); + setPaintImage(); // TODO: move inside panel.xxxx()? + repaint(); + + + + + /*double move = moveStepSize * yWindowSize; + + mins.set(0, 0, move); + mins.set(1, 0, 0); + maxs.set(0, 0, xWindowSize + move); + maxs.set(1, 0, yWindowSize);*/ + x1 = x2; + y1 = y2; + + } if (zoomRegion || idRegion) { statusBar.setText("Highlighting region from (" + x1 + "," + y1 + ") to (" + e.getX() + "," + e.getY() + ")"); @@ -1120,6 +1600,18 @@ public void mouseDragged(MouseEvent e) { } public void mouseClicked(MouseEvent e) { + + if (mode==modes.magnifiermode) + { + x1 = e.getX(); + y1 = e.getY(); + + zoom2Point(x1, y1); + setPaintImage(); + repaint(); + + } + if (zoomPoint) { x1 = e.getX(); y1 = e.getY(); @@ -1128,11 +1620,13 @@ public void mouseClicked(MouseEvent e) { statusBar.setText(statusMessage + ": Zooming to (" + x1 + "," + y1 + ")"); zoom2Point(x1, y1); + setPaintImage(); repaint(); } else if (SwingUtilities.isRightMouseButton(e)) { statusBar.setText(statusMessage + ": Zooming out from (" + x1 + "," + y1 + ")"); zoomOutFromPoint(x1, y1); + setPaintImage(); repaint(); } else { statusBar @@ -1142,6 +1636,10 @@ public void mouseClicked(MouseEvent e) { } public void mouseMoved(MouseEvent e) { + + //System.out.println(e.getX()+" "+e.getY()); + + } public void mouseEntered(MouseEvent e) { @@ -1150,4 +1648,50 @@ public void mouseEntered(MouseEvent e) { public void mouseExited(MouseEvent e) { } + public void mouseWheelMoved(MouseWheelEvent e) { + + if (mode==modes.magnifiermode) + { + int notches = e.getWheelRotation(); + x1 = e.getX(); + y1 = e.getY(); + if (notches < 0) + { + + zoom2Point(x1, y1); + setPaintImage(); + repaint(); + /*VertexFitter f = new VertexFitter(); + zoomIn(f); + applyFit(f); + setPaintImage(); // TODO: move inside panel.xxxx()? + repaint();*/ + } + else + { + zoomOutFromPoint(x1, y1); + setPaintImage(); + repaint(); + /*VertexFitter f = new VertexFitter(); + zoomOut(f); + applyFit(f); + setPaintImage(); // TODO: move inside panel.xxxx()? + repaint();*/ + + } + + } + String message; + /*int notches = e.getWheelRotation(); + if (notches < 0) { + message = "Mouse wheel moved UP " + + -notches + " notch(es)\n"; + } else { + message = "Mouse wheel moved DOWN " + + notches + " notch(es)\n"; + } + System.out.println(message);*/ + + } + } diff --git a/Java/src/Viewer2D/FileInputHandler.java b/Java/src/Viewer2D/FileInputHandler.java index 88ac8766..55eaa351 100755 --- a/Java/src/Viewer2D/FileInputHandler.java +++ b/Java/src/Viewer2D/FileInputHandler.java @@ -23,6 +23,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.Arrays; import java.util.StringTokenizer; import java.util.Vector; @@ -56,8 +57,8 @@ public FileInputHandler(String filename) throws FileNotFoundException { } // MUTATORS - public void setDelimeters(String delimiters) { - this.delimeters = delimeters; + public void setDelimeters(String delimiters_) { + this.delimeters = delimiters_; } // ACCESSORS @@ -82,12 +83,24 @@ public boolean readNextLine() throws IOException { return true; } - public double getTokenAsDouble(int i) { - return Double.parseDouble((String) tokens.elementAt(i)); + public Double getTokenAsDouble(int i) { + try + { + return Double.parseDouble((String) tokens.elementAt(i)); + } + catch (NumberFormatException nfe) { + return null; + } } - public int getTokenAsInt(int i) { + public Integer getTokenAsInt(int i) { + try + { return Integer.parseInt((String) tokens.elementAt(i)); + } + catch (NumberFormatException nfe) { + return null; + } } public void print() { @@ -100,10 +113,32 @@ private void performSplit() { if (text == null) { return; } - tokenizer = new StringTokenizer(text, delimeters); + tokenizer = new StringTokenizer(text, delimeters,true); + + //String[] stringArr ; + //tokens = new Vector( Arrays.asList(text.split(delimeters))); + boolean lastissep = false; while (tokenizer.hasMoreTokens()) { - tokens.addElement(tokenizer.nextElement()); + Object o = tokenizer.nextElement(); + if (((String)(o)).equals(delimeters)) + { + tokens.addElement(""); + lastissep = true; + } + else + { + tokens.addElement(o); + lastissep = false; + if (tokenizer.hasMoreTokens()) + { + tokenizer.nextElement(); + lastissep = true; + } + } + } + if (lastissep) + tokens.addElement(""); } } diff --git a/Java/src/Viewer2D/FormatVertex.java b/Java/src/Viewer2D/FormatVertex.java index 168fe446..ea699400 100755 --- a/Java/src/Viewer2D/FormatVertex.java +++ b/Java/src/Viewer2D/FormatVertex.java @@ -19,6 +19,10 @@ package Viewer2D; +import java.util.HashMap; + +import Jama.Matrix; + public class FormatVertex { private Vertex[] vertices; private VertexStats stats; @@ -26,15 +30,35 @@ public class FormatVertex { private int threadCount; private static int DIMENSION = Vertex.DIMENSION; private VertexFitter fitter; + private HashMap labels; + private double scaleBy; + private double scaleCorrectionLabels; + private double scaleCorrectionLabelsOriginal; + private boolean scaleLabelUsed; + private double minX, maxX; + private double minY, maxY; + boolean aligncenter; // CONSTRUCTORS - public FormatVertex(Vertex[] e, VertexStats stats, int[] wSizes, + public FormatVertex(Vertex[] e, HashMap labels,double scaleLabels,double minX, double minY,double maxX,double maxY, + boolean aligncenter, + VertexStats stats, int[] wSizes, int threads2use) { vertices = e; this.stats = stats; windowSizes = wSizes; threadCount = threads2use; + this.labels = labels; fitter = new VertexFitter(); + this.scaleCorrectionLabels = scaleLabels; + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + this.aligncenter = aligncenter; + scaleBy = 1; + scaleLabelUsed = false; + } // MUTATORS @@ -44,10 +68,10 @@ public void fitDataToWindow() { // Here the appropriate translation/scaling matrices // must be generated and applied to the originalVertices. // Stretch the vertices to fit the window - scalingIssues(); - + double scale = scalingIssues(); + // Recenter the vertices based on the avg of max and min values - translationIssues(); + translationIssues(scale); applyTransformation(); } @@ -72,6 +96,16 @@ public void setWindowSizeY(int y) { windowSizes[1] = y; } + public void setLabelScale(double scale) + { + + scaleCorrectionLabels = scale/(scaleLabelUsed? scaleCorrectionLabelsOriginal: scaleCorrectionLabels); + scaleLabelUsed = false; + scaleLabels(1); + + } + + public void threads(int t) { threadCount = t; } @@ -92,6 +126,11 @@ public int[] windowSizes() { public int threads() { return threadCount; } + + public double getScale() + { + return scaleBy; + } public VertexFitter getFitter() { return fitter; @@ -106,40 +145,118 @@ public void print() { // PRIVATE METHOD CALLS // ///////////////////////////////////////////////////////////////////// - private void translationIssues() { + private void translationIssues(double scale) { double[] offsets = new double[DIMENSION]; - for (int d = 0; d < DIMENSION; ++d) { - offsets[d] -= stats.min(d); + + if (aligncenter) { + for (int d = 0; d < DIMENSION; ++d) { + offsets[d] = ((double) windowSizes[d]) / (2 * scale) - stats.avg(d); + } + } + else { + for (int d = 0; d < DIMENSION; ++d) { + offsets[d] -= stats.min(d); + } } + Transformer transformer = new Transformer(); transformer.move(offsets); // Add this job to the list fitter.addManipulation(transformer); } - private void scalingIssues() { - double scale = windowSizes[0] / stats.span(0); - for (int d = 1; d < DIMENSION; ++d) { - double newScale = windowSizes[d] / stats.span(d); - if (newScale < scale) { - scale = newScale; - } - } + private double scalingIssues() { + double scale; + + if (minX!=0 && maxX!=0) + scale = windowSizes[0] / ( maxX-minX); + else + scale = windowSizes[0] / stats.span(0); + + + for (int d = 1; d < DIMENSION; ++d) { + double newScale = windowSizes[d] / stats.span(d); + if (newScale < scale) { + scale = newScale; + } + } + Transformer transformer = new Transformer(); transformer.scale(.99 * scale); + // scaleBy = .99 * scale; fitter.addManipulation(transformer); + + return .99 * scale; + } + + private double calculateScale() + { + Matrix m = fitter.getManipulationMatrix(); + double sum = 0; + for (int i = 0; i < Vertex.DIMENSION;i++) + sum += m.get(0, i)*m.get(0, i); + + double scale = Math.sqrt(sum); + + return scale; + } - private void applyTransformation() { + private void scaleLabels(double currentScale) + { + labels.forEach((k,v) -> { + Label l = (Label)v; + Vertex vertex = (Vertex) k; + l.linelength =(l.linelength * currentScale * scaleCorrectionLabels); + //l.linesize =(int) (l.linesize * scaleBy * scaleCorrectionLabels); + l.bottomtextsize = (l.bottomtextsize * currentScale * scaleCorrectionLabels); + l.toptextsize = (l.toptextsize * currentScale * scaleCorrectionLabels); + l.shapesize = (l.shapesize * currentScale * scaleCorrectionLabels); + + }); + if (!scaleLabelUsed) + { + scaleCorrectionLabelsOriginal = scaleCorrectionLabels; + + } + scaleLabelUsed = true; + scaleCorrectionLabels = 1; + + + } + + public void applyTransformation() { + double currentScale = calculateScale(); + scaleBy = scaleBy * currentScale; // Now to do the work to each vertex + long start = System.nanoTime(); + ManipVertexArray manipa[] = new ManipVertexArray[threadCount]; + // VertexStats statsa[] = new VertexStats[threadCount]; + for (int t = 0; t < threadCount; ++t) { - ManipVertexArray manip = new ManipVertexArray(vertices); + ManipVertexArray manip = new ManipVertexArray(vertices,"vertexrecalc"+t); + manipa[t] = manip; + // statsa[t] = new VertexStats(); manip.setFitter(fitter); manip.setStride(threadCount); manip.setOffset(t); manip.setVertexStats(stats); // Fix this if threading (race cond.) - manip.run(); + manip.start(); + } + for (int t = 0; t < threadCount;t++) + { + try { + manipa[t].getThread().join(); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + } + long end = System.nanoTime(); + System.out.println("Time to recalculate vertices "+(end-start)/1000000000.0+"s"); + scaleLabels(currentScale); + } } diff --git a/Java/src/Viewer2D/Label.java b/Java/src/Viewer2D/Label.java new file mode 100644 index 00000000..96834638 --- /dev/null +++ b/Java/src/Viewer2D/Label.java @@ -0,0 +1,91 @@ +package Viewer2D; + +import Jama.Matrix; + +import java.awt.Color; + +public class Label { + + + //private Vertex id; + public double shapesize ; + public String shape ; + public int shapeborderwidth ; + public Color shapebordercolor; + + public Color shapefillcolor ; + + public double shapefillopacity ; + public int linesize ; + public double linelength ; + public double lineangle; + public Color linecolor; + + public String toptextttf; + public double toptextsize ; + public Color toptextcolor ; + public Color topbgfillcolor ; + + public String bottomtextttf ; + public double bottomtextsize ; + public Color bottomtextcolor ; + public Color bottombgfillcolor ; + + public String toptext; + public String bottomtext; + Label( + String shape_ , + int shapesize_ , + int shapeborderwidth_ , + Color shapebordercolor_, + + Color shapefillcolor_ , + + double shapefillopacity_ , + int linesize_ , + int linelength_ , + double lineangle_, + Color linecolor_, + + String toptextttf_, + int toptextsize_ , + Color toptextcolor_ , + Color topbgfillcolor_ , + + String bottomtextttf_ , + int bottomtextsize_ , + Color bottomtextcolor_ , + Color bottombgfillcolor_ , + + String toptext_, + String bottomtext_) + { + //id=id_; + shape = shape_; + shapesize = shapesize_; + shapeborderwidth = shapeborderwidth_; + shapebordercolor = shapebordercolor_; + shapefillcolor = shapefillcolor_; + shapefillopacity = shapefillopacity_; + linesize = linesize_; + linelength = linelength_; + lineangle = lineangle_; + linecolor = linecolor_; + toptextttf = toptextttf_; + toptextsize = toptextsize_; + toptextcolor = toptextcolor_; + topbgfillcolor = topbgfillcolor_; + bottomtextttf = bottomtextttf_; + bottomtextsize = bottomtextsize_; + bottomtextcolor = bottomtextcolor_; + bottombgfillcolor = bottombgfillcolor_; + toptext = toptext_; + bottomtext = bottomtext_; + + } + + + + + +} \ No newline at end of file diff --git a/Java/src/Viewer2D/ManipVertexArray.java b/Java/src/Viewer2D/ManipVertexArray.java index a057d1d5..af270457 100755 --- a/Java/src/Viewer2D/ManipVertexArray.java +++ b/Java/src/Viewer2D/ManipVertexArray.java @@ -22,34 +22,52 @@ import Jama.*; import Jama.Matrix.*; -public class ManipVertexArray { +public class ManipVertexArray implements Runnable{ private int off; // Array offset to start private int stride; // Steps to skip each time private VertexFitter fitter; private Vertex[] vertices; private VertexStats stats; + private Thread t; + private String threadName; + static final Object object = new Object(); // CONSTRUCTORS - public ManipVertexArray( Vertex[] verticesO ) + public ManipVertexArray( Vertex[] verticesO ,String threadName) { // This is setup for 1 thread by default off = 0; stride = 1; vertices = verticesO; + this.threadName = threadName; + } + Thread getThread() + { + return t; } - public void run() { - stats.clear(); + for ( int e = off; e < vertices.length; e += stride ) { fitter.fitVertex( vertices[e] ); - stats.addStatsOfVertex( vertices[e] ); + // synchronized(object) + { + stats.addStatsOfVertex( vertices[e] ); + } } } + public void start () { + System.out.println("Starting " + threadName ); + if (t == null) { + t = new Thread (this, threadName); + t.start (); + } + } + // ACCESSORS public void print() { diff --git a/Java/src/Viewer2D/VertexFitter.java b/Java/src/Viewer2D/VertexFitter.java index 34463ca0..2ab2d58e 100755 --- a/Java/src/Viewer2D/VertexFitter.java +++ b/Java/src/Viewer2D/VertexFitter.java @@ -55,6 +55,8 @@ public void fitVertex( Vertex original ) original.location( manipulation.times( original.location() ) ); } + + // This is to add a manipulation to the 'to do' list. // This permits composite manipulations public void addManipulation( Transformer manip ) diff --git a/Java/src/Viewer2D/VertexStats.java b/Java/src/Viewer2D/VertexStats.java index d37cac2d..72e688f7 100755 --- a/Java/src/Viewer2D/VertexStats.java +++ b/Java/src/Viewer2D/VertexStats.java @@ -129,14 +129,31 @@ public Matrix maxs() { public void print() { System.out.println("\tS T A T S"); - System.out.println("Mins: "); - mins.print(4, 2); - System.out.println("Maxs: "); - maxs.print(4, 2); + System.out.print("Mins: "); + String [] names = {"x:","y:","z:"}; + for (int i = 0; i < DIMENSION;i++) + { + System.out.print(names[i]+" "+mins.get(i,0)); + + if (i!=DIMENSION) + System.out.print(" "); + } + System.out.println(); + //mins.print(4, 2); + System.out.print("Maxs: "); + //maxs.print(4, 2); + for (int i = 0; i < DIMENSION;i++) + { + System.out.print(names[i]+" "+maxs.get(i,0)); + + if (i!=DIMENSION) + System.out.print(" "); + } + System.out.println(); System.out.println("Totals: "); - totals.print(4, 2); + //totals.print(4, 2); for (int ii = 0; ii < DIMENSION; ++ii) { - System.out.println("Avg" + ii + ": " + avg(ii)); + System.out.println("Avg " + names[ii] + " " + avg(ii)); } System.out.println("Vertices Counted: " + vertexCtr); } diff --git a/Java/src/Viewer2D/Viewer2D.java b/Java/src/Viewer2D/Viewer2D.java index 01ebeec9..0e7b6712 100755 --- a/Java/src/Viewer2D/Viewer2D.java +++ b/Java/src/Viewer2D/Viewer2D.java @@ -21,9 +21,15 @@ import java.io.File; +import javax.swing.InputMap; +import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; +import ImageMaker.GenerateImages; + +import java.awt.Insets; + /** * SESS - 2014.05.11: *
    @@ -49,6 +55,9 @@ public static void main(String[] args) { } catch (Exception e) { e.printStackTrace(); } + String OS = System.getProperty("os.name").toLowerCase(); + //if (OS.contains("win")) + // UIManager.put("MenuItem.margin", new Insets(2, -15, 2, 2)); } else { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if (laf.equals(info.getName())) { @@ -62,22 +71,31 @@ public static void main(String[] args) { } } + InputMap im = (InputMap)UIManager.get("Button.focusInputMap"); + im.put(KeyStroke.getKeyStroke("pressed SPACE"), "none"); + im.put(KeyStroke.getKeyStroke("released SPACE"), "none"); + // create a new frame object EdgesFrame frame = new EdgesFrame("lglview", IMAGE_SIZE, IMAGE_SIZE); + GenerateImages.ParseArguments pa = new GenerateImages.ParseArguments(true); + pa.parse(args); + // Deal with possible command line args // The first entry is a possible edges file (.ls) and the next // file is a possible coord file - if (args.length > 0) { - System.out.println("Loading edges file " + args[0]); - frame.loadSHORTFile(new File(args[0])); - if (args.length > 1) { - System.out.println("Loading coords file " + args[1]); + if (args.length > 0) + { + System.out.println("Loading edges file " + pa.edgeFile); + frame.loadSHORTFile(new File(pa.edgeFile)); + //if (args.length > 1) + { + System.out.println("Loading coords file " + pa.coordFiles.get(0)); frame.loadCoordsFile(new File(args[1])); } - if (args.length > 2) { - System.out.println("Loading color file " + args[2]); - File colorfile = new File(args[2]); + if (!pa.edgeColorFile.isEmpty()) { + System.out.println("Loading color file " + pa.edgeColorFile); + File colorfile = new File(pa.edgeColorFile); frame.loadEdgeColorFile(colorfile); } } diff --git a/Java/src/Viewer2D/ViewerIO.java b/Java/src/Viewer2D/ViewerIO.java index 8cbf7132..31ea9276 100755 --- a/Java/src/Viewer2D/ViewerIO.java +++ b/Java/src/Viewer2D/ViewerIO.java @@ -63,11 +63,59 @@ public class ViewerIO { private HashMap vertexColorMap; private Vector edgesV; private Vector verticesV; + private HashMap labelMap; + private double scalingLabel; + private double customMinX; + private double customMaxX; + private double customMinY; + private double customMaxY; public ViewerIO(File filename) throws FileNotFoundException, IOException { file = filename; edgeColorMap = new HashMap(); vertexColorMap = new HashMap(); + labelMap = new HashMap(); + scalingLabel = 1; + customMinX = 0; + customMaxX = 0; + customMinY = 0; + customMaxY = 0; + + } + + public void setLabelScale(double scale) + { + this.scalingLabel = scale; + } + + public void setMinMaxXY(double minx, double miny,double maxx, double maxy) + { + customMinX = minx; + customMaxX = maxx; + customMinY = miny; + customMaxY = maxy; + + } + + public double getMinX() { + return customMinX; + } + + public double getMaxX() { + return customMaxX; + } + + public double getMinY() { + return customMinY; + } + + public double getMaxY() { + return customMaxY; + } + + + public double getLabelScale() { + return this.scalingLabel; } // TODO: SESS - should move to "utils" class? @@ -240,6 +288,128 @@ public void loadVertexColorFile(File f) throws IOException, } } + public void clearLabels() + { + labelMap.clear(); + } + + public void loadLabelFile(File f) throws IOException,FileNotFoundException { + if (edges == null || vertices == null) { + return; + } + labelMap.clear(); + fileio = new FileInputHandler(f.getAbsolutePath()); + fileio.setDelimeters(","); + while (fileio.readNextLine()) { + int s = fileio.getTokenCount(); + if (s != 21) { + throw new IOException("number of fields are not correct in the file"); + } + + int index = 0; + String id = fileio.getToken(index++); + String shape = fileio.getToken(index++); + Integer shapesize = fileio.getTokenAsInt(index++); + Integer shapeboarderwidth = fileio.getTokenAsInt(index++); + + + + Color shapebordercolor = readColorRGBHex(index++); + + Color shapefillcolor = readColorRGBHexAlpha(index++); + index++; + double shapefillopacity = 0;//fileio.getTokenAsDouble(index++); + if (shapesize == null || shapeboarderwidth == null || shapebordercolor == null || shapefillcolor == null) + { + shapesize = 0; + shapeboarderwidth = 0; + shape = ""; + } + + + Integer linesize = fileio.getTokenAsInt(index++); + Integer linelength = fileio.getTokenAsInt(index++); + Double lineangle = fileio.getTokenAsDouble(index++); + Color linecolor = readColorRGBHex(index++); + + + + + String toptextttf = fileio.getToken(index++); + Integer toptextsize = fileio.getTokenAsInt(index++); + Color toptextcolor = readColorRGBHex(index++); + Color topbgfillcolor =readColorRGBHex(index++); + + String bottomtextttf = fileio.getToken(index++); + Integer bottomtextsize = fileio.getTokenAsInt(index++); + Color bottomtextcolor = readColorRGBHex(index++); + Color bottombgfillcolor =readColorRGBHex(index++); + + String toptext = fileio.getToken(index++); + String bottomtext = fileio.getToken(index++); + + + if (toptextttf.isEmpty() || toptextsize== null || toptextcolor == null) + { + toptext = ""; + toptextsize = 0; + } + + if (bottomtextttf.isEmpty() || bottomtextsize== null || bottomtextcolor == null) + { + bottomtext = ""; + bottomtextsize = 0; + } + + + + if (linesize == null) + { + linecolor = null; + linesize = 0; + } + if (lineangle ==null || linelength== null ) + { + //toptext = ""; + //bottomtext = ""; + lineangle = 0.0; + linelength = 0; + } + + + + Object o = vertexIdMap.get(id); + if (o == null) { + System.out.println("Undefined Vertex: " + id); + continue; + } + Label l = new Label(shape, + shapesize , + shapeboarderwidth , + shapebordercolor, + shapefillcolor , + shapefillopacity , + linesize , + linelength , + lineangle , + linecolor, + toptextttf, + toptextsize , + toptextcolor , + topbgfillcolor , + bottomtextttf , + bottomtextsize , + bottomtextcolor , + bottombgfillcolor , + toptext, + bottomtext); + labelMap.put((Vertex) o,l); + + } + + } + + // ////////////////////////////////////////////////////////// // ACCESSORS // ////////////////////////////////////////////////////////// @@ -256,6 +426,11 @@ public Vertex[] getVertices() { return vertices; } + public HashMap getLabels() { + return labelMap; + } + + public HashMap getEdgeColorMap() { return edgeColorMap; } @@ -345,6 +520,37 @@ private Color readColorRGB(int index) { return new Color((float) r, (float) g, (float) b); } + private Color readColorRGBHex(int index) { + String hex = fileio.getToken(index); + try + { + int code = Integer.parseInt(hex,16); + + return new Color((float) (code >> 16)/255, (float) ((code >> 8) & 255)/255, (float) (code & 255)/255); + } + catch (NumberFormatException nfe) { + return null; + } + } + + private Color readColorRGBHexAlpha(int index) { + String hex = fileio.getToken(index++); + try + { + int code = Integer.parseInt(hex,16); + Double d = fileio.getTokenAsDouble(index); + if (d==null) + return null; + float alpha = (float) d.floatValue(); + return new Color((float) (code >> 16)/255, (float) ((code >> 8) & 255)/255, (float) (code & 255)/255,alpha/100); + } + catch (NumberFormatException nfe) { + return null; + } + + } + + private void tidyNewVerticesAndEdges() { // This just loads the edges into an array and out of the // vector @@ -363,4 +569,6 @@ private void tidyNewVerticesAndEdges() { } verticesV.clear(); } + + } diff --git a/Java/src/Viewer2D/icons8-magnifier-67_32x32.png b/Java/src/Viewer2D/icons8-magnifier-67_32x32.png new file mode 100644 index 00000000..a735819b Binary files /dev/null and b/Java/src/Viewer2D/icons8-magnifier-67_32x32.png differ diff --git a/Java/src/de/erichseifert/vectorgraphics2d/Document.java b/Java/src/de/erichseifert/vectorgraphics2d/Document.java new file mode 100644 index 00000000..283f4777 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/Document.java @@ -0,0 +1,39 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface for documents that are able to output binary files in the + * implemented file format. + */ +public interface Document { + void writeTo(OutputStream out) throws IOException; + + /** + * Returns whether or not the {@code Document} represents compressed data. + * @return {@code true} if the contents are compressed, {@code false} otherwise. + */ + boolean isCompressed(); +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/GraphicsState.java b/Java/src/de/erichseifert/vectorgraphics2d/GraphicsState.java new file mode 100644 index 00000000..5fc442f1 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/GraphicsState.java @@ -0,0 +1,255 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; + +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; + +/** + * Representation of the Graphics2D status at a certain point in time. + */ +public class GraphicsState implements Cloneable { + /** Default background color. */ + public static final Color DEFAULT_BACKGROUND = Color.BLACK; + /** Default color. */ + public static final Color DEFAULT_COLOR = Color.WHITE; + /** Default clipping shape. */ + public static final Shape DEFAULT_CLIP = null; + /** Default composite mode. */ + public static final Composite DEFAULT_COMPOSITE = AlphaComposite.SrcOver; + /** Default font. */ + public static final Font DEFAULT_FONT = Font.decode(null); + /** Default paint. */ + public static final Color DEFAULT_PAINT = DEFAULT_COLOR; + /** Default stroke. */ + public static final Stroke DEFAULT_STROKE = new BasicStroke(); + /** Default transformation. */ + public static final AffineTransform DEFAULT_TRANSFORM = + new AffineTransform(); + /** Default XOR mode. */ + public static final Color DEFAULT_XOR_MODE = Color.BLACK; + + /** Rendering hints. */ + private RenderingHints hints; + /** Current background color. */ + private Color background; + /** Current foreground color. */ + private Color color; + /** Shape used for clipping paint operations. */ + private Shape clip; + /** Method used for compositing. */ + private Composite composite; + /** Current font. */ + private Font font; + /** Paint used to fill shapes. */ + private Paint paint; + /** Stroke used for drawing shapes. */ + private Stroke stroke; + /** Current transformation matrix. */ + private AffineTransform transform; + /** XOR mode used for rendering. */ + private Color xorMode; + + public GraphicsState() { + hints = new RenderingHints(null); + background = DEFAULT_BACKGROUND; + color = DEFAULT_COLOR; + clip = DEFAULT_CLIP; + composite = DEFAULT_COMPOSITE; + font = DEFAULT_FONT; + paint = DEFAULT_PAINT; + stroke = DEFAULT_STROKE; + transform = new AffineTransform(DEFAULT_TRANSFORM); + xorMode = DEFAULT_XOR_MODE; + } + + @Override + public Object clone() throws CloneNotSupportedException { + GraphicsState clone = (GraphicsState) super.clone(); + clone.hints = (RenderingHints) hints.clone(); + clone.clip = GraphicsUtils.clone(clip); + clone.transform = new AffineTransform(transform); + return clone; + } + + private static Shape transformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + if (tx == null || tx.isIdentity()) { + return GraphicsUtils.clone(s); + } + boolean isRectangle = s instanceof Rectangle2D; + int nonRectlinearTxMask = AffineTransform.TYPE_GENERAL_TRANSFORM | + AffineTransform.TYPE_GENERAL_ROTATION; + boolean isRectlinearTx = (tx.getType() & nonRectlinearTxMask) == 0; + if (isRectangle && isRectlinearTx) { + Rectangle2D rect = (Rectangle2D) s; + double[] corners = new double[] { + rect.getMinX(), rect.getMinY(), + rect.getMaxX(), rect.getMaxY() + }; + tx.transform(corners, 0, corners, 0, 2); + rect = new Rectangle2D.Double(); + rect.setFrameFromDiagonal(corners[0], corners[1], corners[2], + corners[3]); + return rect; + } + return tx.createTransformedShape(s); + } + + private static Shape untransformShape(Shape s, AffineTransform tx) { + if (s == null) { + return null; + } + try { + AffineTransform inverse = tx.createInverse(); + return transformShape(s, inverse); + } catch (NoninvertibleTransformException e) { + return null; + } + } + + public Shape transformShape(Shape shape) { + return transformShape(shape, transform); + } + + public Shape untransformShape(Shape shape) { + return untransformShape(shape, transform); + } + + public RenderingHints getHints() { + return hints; + } + + public Color getBackground() { + return background; + } + + public void setBackground(Color background) { + this.background = background; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public Shape getClip() { + return untransformShape(clip); + } + + public void setClip(Shape clip) { + this.clip = transformShape(clip); + } + + public Composite getComposite() { + return composite; + } + + public void setComposite(Composite composite) { + this.composite = composite; + } + + public Font getFont() { + return font; + } + + public void setFont(Font font) { + this.font = font; + } + + public Paint getPaint() { + return paint; + } + + public void setPaint(Paint paint) { + this.paint = paint; + } + + public Stroke getStroke() { + return stroke; + } + + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + public AffineTransform getTransform() { + return new AffineTransform(transform); + } + + public void setTransform(AffineTransform tx) { + transform.setTransform(tx); + } + + public Color getXorMode() { + return xorMode; + } + + public void setXorMode(Color xorMode) { + this.xorMode = xorMode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GraphicsState)) { + return false; + } + GraphicsState o = (GraphicsState) obj; + // Compare all attributes + return !(!hints.equals(o.hints) || !background.equals(o.background) || + !color.equals(o.color) || !composite.equals(o.composite) || + !font.equals(o.font) || !paint.equals(o.paint) || + !stroke.equals(o.stroke) || !transform.equals(o.transform) || + !xorMode.equals(o.xorMode) || + ((clip == null || o.clip == null) && clip != o.clip) || + (clip != null && !clip.equals(o.clip))); + } + + public boolean isDefault() { + return hints.isEmpty() && background.equals(DEFAULT_BACKGROUND) && + color.equals(DEFAULT_COLOR) && composite.equals(DEFAULT_COMPOSITE) && + font.equals(DEFAULT_FONT) && paint.equals(DEFAULT_PAINT) && + stroke.equals(DEFAULT_STROKE) && transform.equals(DEFAULT_TRANSFORM) && + xorMode.equals(DEFAULT_XOR_MODE) && clip == DEFAULT_CLIP; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/Processor.java b/Java/src/de/erichseifert/vectorgraphics2d/Processor.java new file mode 100644 index 00000000..b40a0cfb --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/Processor.java @@ -0,0 +1,39 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * Translates {@link Command} objects into a {@link Document}. + */ +public interface Processor { + /** + * Constructs a {@code Document} from the specified commands. + * @param commands Commands used to create the {@code Document}. + * @param pageSize Size of the resulting {@code Document}. + * @return {@code Document} representation of the commands. + */ + Document getDocument(CommandSequence commands, PageSize pageSize); +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/Processors.java b/Java/src/de/erichseifert/vectorgraphics2d/Processors.java new file mode 100644 index 00000000..5fffcc69 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/Processors.java @@ -0,0 +1,59 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import de.erichseifert.vectorgraphics2d.eps.EPSProcessor; +import de.erichseifert.vectorgraphics2d.pdf.PDFProcessor; +import de.erichseifert.vectorgraphics2d.svg.SVGProcessor; + +/** + *

    Utility class that provides simplified access to processors for different + * file formats. At the moment three implementations of processors are available: + * {@code "eps"}, {@code "pdf"}, and {@code "svg"}

    + *

    A new processor can be retrieved by calling the {@link #get(String)} + * method with the format name:

    + *
    Processor pdfProcessor = Processors.get("pdf");
    + */ +public abstract class Processors { + /** + * Default constructor that prevents creation of class. + */ + Processors() { + throw new UnsupportedOperationException(); + } + + public static Processor get(String format) { + if (format == null) { + throw new NullPointerException("Format cannot be null."); + } + switch (format) { + case "eps": + return new EPSProcessor(); + case "pdf": + return new PDFProcessor(true); + case "svg": + return new SVGProcessor(); + default: + throw new IllegalArgumentException("Unknown format \"" + format + "\""); + } + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/SizedDocument.java b/Java/src/de/erichseifert/vectorgraphics2d/SizedDocument.java new file mode 100644 index 00000000..9c4129aa --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/SizedDocument.java @@ -0,0 +1,46 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * Abstract base for documents that are restricted to a specified page size. + */ +public abstract class SizedDocument implements Document { + private final PageSize pageSize; + private final boolean compressed; + + public SizedDocument(PageSize pageSize, boolean compressed) { + this.pageSize = pageSize; + this.compressed = compressed; + } + + public PageSize getPageSize() { + return pageSize; + } + + @Override + public boolean isCompressed() { + return this.compressed; + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/VectorGraphics2D.java b/Java/src/de/erichseifert/vectorgraphics2d/VectorGraphics2D.java new file mode 100644 index 00000000..864e3074 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/VectorGraphics2D.java @@ -0,0 +1,804 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.RenderingHints.Key; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; +import java.util.Map.Entry; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.MutableCommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.CreateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawImageCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawStringCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.FillShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.RotateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.ScaleCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetBackgroundCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetClipCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetColorCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetCompositeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetFontCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetHintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetPaintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetStrokeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetXORModeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.ShearCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.TransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.TranslateCommand; +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; + +/** + *

    Implementation of the Graphics2D interface to Java to generate a sequence of + * commands. An instance of {@code VectorGraphics2D} can be used to replace any + * {@code Graphics2D} object. It can be created with its standard constructor:

    + *
    Graphics2D g = new VectorGraphics2D();
    + * + * @see http://www.java2s.com/Code/Java/2D-Graphics-GUI/YourownGraphics2D.htm + */ +public class VectorGraphics2D extends Graphics2D implements Cloneable { + private final MutableCommandSequence commands; + /** Device configuration settings. */ + private final GraphicsConfiguration deviceConfig; + /** Context settings used to render fonts. */ + private final FontRenderContext fontRenderContext; + /** Flag that tells whether this graphics object has been disposed. */ + private boolean disposed; + + private GraphicsState state; + + public VectorGraphics2D() { + this.commands = new MutableCommandSequence(); + emit(new CreateCommand(this)); + GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (!graphicsEnvironment.isHeadlessInstance()) { + GraphicsDevice graphicsDevice = graphicsEnvironment.getDefaultScreenDevice(); + deviceConfig = graphicsDevice.getDefaultConfiguration(); + } else { + deviceConfig = null; + } + fontRenderContext = new FontRenderContext(null, false, true); + + state = new GraphicsState(); + + // Ensure that document state matches default state of Graphics2D + // TODO: Default graphics state does not need to be printed in the document. + // Use filters in the appropriate documents + setColor(Color.BLACK); // Required for EPS, PDF, and SVG + setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f)); // EPS and PDF + } + + @Override + public Object clone() throws CloneNotSupportedException { + VectorGraphics2D clone = (VectorGraphics2D) super.clone(); + clone.state = (GraphicsState) state.clone(); + return clone; + } + + @Override + public void addRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + for (Entry entry : hints.entrySet()) { + setRenderingHint((Key) entry.getKey(), entry.getValue()); + } + } + + @Override + public void clip(Shape s) { + Shape clip = getClip(); + if ((clip != null) && (s != null)) { + s = intersectShapes(clip, s); + } + setClip(s); + } + + private static Shape intersectShapes(Shape s1, Shape s2) { + if (s1 instanceof Rectangle2D && s2 instanceof Rectangle2D) { + Rectangle2D r1 = (Rectangle2D) s1; + Rectangle2D r2 = (Rectangle2D) s2; + double x1 = Math.max(r1.getMinX(), r2.getMinX()); + double y1 = Math.max(r1.getMinY(), r2.getMinY()); + double x2 = Math.min(r1.getMaxX(), r2.getMaxX()); + double y2 = Math.min(r1.getMaxY(), r2.getMaxY()); + + Rectangle2D intersection = new Rectangle2D.Double(); + if ((x2 < x1) || (y2 < y1)) { + intersection.setFrameFromDiagonal(0, 0, 0, 0); + } else { + intersection.setFrameFromDiagonal(x1, y1, x2, y2); + } + return intersection; + } else { + Area intersection = new Area(s1); + intersection.intersect(new Area(s2)); + return intersection; + } + } + + @Override + public void draw(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new DrawShapeCommand(s)); + } + + @Override + public void drawGlyphVector(GlyphVector g, float x, float y) { + Shape s = g.getOutline(x, y); + draw(s); + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + BufferedImage bimg = getTransformedImage(img, xform); + return drawImage(bimg, bimg.getMinX(), bimg.getMinY(), + bimg.getWidth(), bimg.getHeight(), null, null); + } + + /** + * Returns a transformed version of an image. + * @param image Image to be transformed + * @param xform Affine transform to be applied + * @return Image with transformed content + */ + private BufferedImage getTransformedImage(Image image, + AffineTransform xform) { + Integer interpolationType = + (Integer) getRenderingHint(RenderingHints.KEY_INTERPOLATION); + if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; + } else if (RenderingHints.VALUE_INTERPOLATION_BILINEAR + .equals(interpolationType)) { + interpolationType = AffineTransformOp.TYPE_BILINEAR; + } else { + interpolationType = AffineTransformOp.TYPE_BICUBIC; + } + AffineTransformOp op = new AffineTransformOp(xform, interpolationType); + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + return op.filter(bufferedImage, null); + } + + @Override + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + if (op != null) { + img = op.filter(img, null); + } + drawImage(img, x, y, img.getWidth(), img.getHeight(), null, null); + } + + @Override + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + drawRenderedImage(img.createDefaultRendering(), xform); + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + BufferedImage bimg = GraphicsUtils.toBufferedImage(img); + drawImage(bimg, xform, null); + } + + @Override + public void drawString(String str, int x, int y) { + drawString(str, (float) x, (float) y); + } + + @Override + public void drawString(String str, float x, float y) { + if (isDisposed() || str == null || str.trim().length() == 0) { + return; + } + boolean isTextAsVectors = false; + if (isTextAsVectors) { + TextLayout layout = new TextLayout(str, getFont(), + getFontRenderContext()); + Shape s = layout.getOutline( + AffineTransform.getTranslateInstance(x, y)); + fill(s); + } else { + emit(new DrawStringCommand(str, x, y)); + } + + } + + @Override + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + drawString(iterator, (float) x, (float) y); + } + + @Override + public void drawString(AttributedCharacterIterator iterator, float x, + float y) { + // TODO Draw styled text + StringBuilder buf = new StringBuilder(); + for (char c = iterator.first(); c != AttributedCharacterIterator.DONE; + c = iterator.next()) { + buf.append(c); + } + drawString(buf.toString(), x, y); + } + + @Override + public void fill(Shape s) { + if (isDisposed() || s == null) { + return; + } + emit(new FillShapeCommand(s)); + } + + @Override + public Color getBackground() { + return state.getBackground(); + } + + @Override + public Composite getComposite() { + return state.getComposite(); + } + + @Override + public GraphicsConfiguration getDeviceConfiguration() { + return deviceConfig; + } + + @Override + public FontRenderContext getFontRenderContext() { + return fontRenderContext; + } + + @Override + public Paint getPaint() { + return state.getPaint(); + } + + @Override + public Object getRenderingHint(Key hintKey) { + if (RenderingHints.KEY_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_TEXT_ANTIALIASING.equals(hintKey)) { + return RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + } else if (RenderingHints.KEY_FRACTIONALMETRICS.equals(hintKey)) { + return RenderingHints.VALUE_FRACTIONALMETRICS_ON; + } + return state.getHints().get(hintKey); + } + + @Override + public RenderingHints getRenderingHints() { + return (RenderingHints) state.getHints().clone(); + } + + @Override + public Stroke getStroke() { + return state.getStroke(); + } + + @Override + public boolean hit(Rectangle rect, Shape s, boolean onStroke) { + Shape hitShape = s; + if (onStroke) { + hitShape = getStroke().createStrokedShape(hitShape); + } + hitShape = state.transformShape(hitShape); + return hitShape.intersects(rect); + } + + @Override + public void setBackground(Color color) { + if (isDisposed() || color == null || getColor().equals(color)) { + return; + } + emit(new SetBackgroundCommand(color)); + state.setBackground(color); + } + + @Override + public void setComposite(Composite comp) { + if (isDisposed()) { + return; + } + if (comp == null) { + throw new IllegalArgumentException("Cannot set a null composite."); + } + emit(new SetCompositeCommand(comp)); + state.setComposite(comp); + } + + @Override + public void setPaint(Paint paint) { + if (isDisposed() || paint == null) { + return; + } + if (paint instanceof Color) { + setColor((Color) paint); + return; + } + if (getPaint().equals(paint)) { + return; + } + emit(new SetPaintCommand(paint)); + state.setPaint(paint); + } + + @Override + public void setRenderingHint(Key hintKey, Object hintValue) { + if (isDisposed()) { + return; + } + state.getHints().put(hintKey, hintValue); + emit(new SetHintCommand(hintKey, hintValue)); + } + + @Override + public void setRenderingHints(Map hints) { + if (isDisposed()) { + return; + } + state.getHints().clear(); + for (Entry hint : hints.entrySet()) { + setRenderingHint((Key) hint.getKey(), hint.getValue()); + } + } + + @Override + public void setStroke(Stroke s) { + if (isDisposed()) { + return; + } + if (s == null) { + throw new IllegalArgumentException("Cannot set a null stroke."); + } + emit(new SetStrokeCommand(s)); + state.setStroke(s); + } + + @Override + public AffineTransform getTransform() { + return new AffineTransform(state.getTransform()); + } + + @Override + public void setTransform(AffineTransform tx) { + if (isDisposed() || tx == null || state.getTransform().equals(tx)) { + return; + } + emit(new SetTransformCommand(tx)); + state.setTransform(tx); + } + + @Override + public void shear(double shx, double shy) { + if (shx == 0.0 && shy == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.shear(shx, shy); + emit(new ShearCommand(shx, shy)); + state.setTransform(txNew); + } + + @Override + public void transform(AffineTransform tx) { + if (tx.isIdentity()) { + return; + } + AffineTransform txNew = getTransform(); + txNew.concatenate(tx); + emit(new TransformCommand(tx)); + state.setTransform(txNew); + } + + @Override + public void translate(int x, int y) { + translate((double) x, (double) y); + } + + @Override + public void translate(double tx, double ty) { + if (tx == 0.0 && ty == 0.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.translate(tx, ty); + emit(new TranslateCommand(tx, ty)); + state.setTransform(txNew); + } + + @Override + public void rotate(double theta) { + rotate(theta, 0.0, 0.0); + } + + @Override + public void rotate(double theta, double x, double y) { + if (theta == 0.0) { + return; + } + + AffineTransform txNew = getTransform(); + if (x == 0.0 && y == 0.0) { + txNew.rotate(theta); + } else { + txNew.rotate(theta, x, y); + } + + emit(new RotateCommand(theta, x, y)); + state.setTransform(txNew); + } + + @Override + public void scale(double sx, double sy) { + if (sx == 1.0 && sy == 1.0) { + return; + } + AffineTransform txNew = getTransform(); + txNew.scale(sx, sy); + emit(new ScaleCommand(sx, sy)); + state.setTransform(txNew); + } + + @Override + public void clearRect(int x, int y, int width, int height) { + Color colorOld = getColor(); + setColor(getBackground()); + fillRect(x, y, width, height); + setColor(colorOld); + } + + @Override + public void clipRect(int x, int y, int width, int height) { + clip(new Rectangle(x, y, width, height)); + } + + @Override + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + // TODO Implement + //throw new UnsupportedOperationException("copyArea() isn't supported by VectorGraphics2D."); + } + + @Override + public Graphics create() { + if (isDisposed()) { + return null; + } + VectorGraphics2D clone = null; + try { + clone = (VectorGraphics2D) this.clone(); + emit(new CreateCommand(clone)); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return clone; + } + + @Override + public void dispose() { + if (isDisposed()) { + return; + } + + emit(new DisposeCommand(this)); + + disposed = true; + } + + @Override + public void drawArc(int x, int y, int width, int height, int startAngle, + int arcAngle) { + draw(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.OPEN)); + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, + ImageObserver observer) { + return drawImage(img, x, y, img.getWidth(observer), + img.getHeight(observer), bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + ImageObserver observer) { + return drawImage(img, x, y, width, height, null, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, + Color bgcolor, ImageObserver observer) { + if (isDisposed() || img == null) { + return true; + } + + int imageWidth = img.getWidth(observer); + int imageHeight = img.getHeight(observer); + Rectangle bounds = new Rectangle(x, y, width, height); + + if (bgcolor != null) { + // Fill rectangle with bgcolor + Color bgcolorOld = getColor(); + setColor(bgcolor); + fill(bounds); + setColor(bgcolorOld); + } + + emit(new DrawImageCommand(img, imageWidth, imageHeight, x, y, width, height)); + return true; + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { + return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, + observer); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, Color bgcolor, + ImageObserver observer) { + if (img == null) { + return true; + } + + int sx = Math.min(sx1, sx2); + int sy = Math.min(sy1, sy2); + int sw = Math.abs(sx2 - sx1); + int sh = Math.abs(sy2 - sy1); + int dx = Math.min(dx1, dx2); + int dy = Math.min(dy1, dy2); + int dw = Math.abs(dx2 - dx1); + int dh = Math.abs(dy2 - dy1); + + // Draw image on rectangle + BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img); + Image cropped = bufferedImg.getSubimage(sx, sy, sw, sh); + return drawImage(cropped, dx, dy, dw, dh, bgcolor, observer); + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + draw(new Line2D.Double(x1, y1, x2, y2)); + } + + @Override + public void drawOval(int x, int y, int width, int height) { + draw(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void drawPolygon(Polygon p) { + draw(p); + } + + @Override + public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { + draw(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { + Path2D p = new Path2D.Float(); + for (int i = 0; i < nPoints; i++) { + if (i > 0) { + p.lineTo(xPoints[i], yPoints[i]); + } else { + p.moveTo(xPoints[i], yPoints[i]); + } + } + draw(p); + } + + @Override + public void drawRect(int x, int y, int width, int height) { + draw(new Rectangle(x, y, width, height)); + } + + @Override + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + draw(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + fill(new Arc2D.Double(x, y, width, height, + startAngle, arcAngle, Arc2D.PIE)); + } + + @Override + public void fillOval(int x, int y, int width, int height) { + fill(new Ellipse2D.Double(x, y, width, height)); + } + + @Override + public void fillPolygon(Polygon p) { + fill(p); + } + + @Override + public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { + fill(new Polygon(xPoints, yPoints, nPoints)); + } + + @Override + public void fillRect(int x, int y, int width, int height) { + fill(new Rectangle(x, y, width, height)); + } + + @Override + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) { + fill(new RoundRectangle2D.Double(x, y, width, height, + arcWidth, arcHeight)); + } + + @Override + public Shape getClip() { + return state.getClip(); + } + + @Override + public Rectangle getClipBounds() { + if (getClip() == null) { + return null; + } + return getClip().getBounds(); + } + + @Override + public Color getColor() { + return state.getColor(); + } + + @Override + public Font getFont() { + return state.getFont(); + } + + @Override + public FontMetrics getFontMetrics(Font f) { + BufferedImage bi = + new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = bi.getGraphics(); + FontMetrics fontMetrics = g.getFontMetrics(f); + g.dispose(); + return fontMetrics; + } + + @Override + public void setClip(Shape clip) { + if (isDisposed()) { + return; + } + emit(new SetClipCommand(clip)); + state.setClip(clip); + } + + @Override + public void setClip(int x, int y, int width, int height) { + setClip(new Rectangle(x, y, width, height)); + } + + @Override + public void setColor(Color c) { + if (isDisposed() || c == null || getColor().equals(c)) { + return; + } + emit(new SetColorCommand(c)); + state.setColor(c); + state.setPaint(c); + } + + @Override + public void setFont(Font font) { + if (isDisposed() || (font != null && getFont().equals(font))) { + return; + } + emit(new SetFontCommand(font)); + state.setFont(font); + } + + @Override + public void setPaintMode() { + setComposite(AlphaComposite.SrcOver); + } + + public Color getXORMode() { + return state.getXorMode(); + } + + @Override + public void setXORMode(Color c1) { + if (isDisposed() || c1 == null) { + return; + } + emit(new SetXORModeCommand(c1)); + state.setXorMode(c1); + } + + private void emit(Command command) { + commands.add(command); + } + + protected boolean isDisposed() { + return disposed; + } + + /** + * Returns a {@code CommandSequence} representing all calls that were issued to this {@code VectorGraphics2D} object. + * @return Sequence of commands since. + */ + public CommandSequence getCommands() { + return commands; + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/VectorHints.java b/Java/src/de/erichseifert/vectorgraphics2d/VectorHints.java new file mode 100644 index 00000000..09eabe41 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/VectorHints.java @@ -0,0 +1,117 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d; + +import java.awt.RenderingHints; +import java.util.HashSet; +import java.util.Set; + +/** + * Hints to control quality settings and choices for vector graphics output. + */ +public abstract class VectorHints { + protected VectorHints() { + throw new UnsupportedOperationException(); + } + + /** + * Base type of all keys used along with the {@link VectorHints} class to + * control algorithm and output choices in the vector graphics output. + */ + public static class Key extends RenderingHints.Key { + private final String description; + + public Key(int privateKey, String description) { + super(privateKey); + this.description = description; + } + + public int getIndex() { + return intKey(); + } + + @Override + public boolean isCompatibleValue(Object val) { + return val instanceof Value && ((Value) val).isCompatibleKey(this); + } + + @Override + public String toString() { + return description; + } + } + + /** + * Base type of all values used along with the {@link VectorHints} class to + * control algorithm and output choices in the vector graphics output. + */ + public static class Value { + private static final Set values = new HashSet<>(); + + private synchronized static void register(Value value) { + String id = value.getId(); + if (values.contains(id)) { + throw new ExceptionInInitializerError( + "Duplicate index: "+value.getIndex()); + } + values.add(id); + } + + private final Key key; + private final int index; + private final String description; + + public Value(Key key, int index, String description) { + this.key = key; + this.index = index; + this.description = description; + register(this); + } + + public boolean isCompatibleKey(RenderingHints.Key key) { + return this.key == key; + } + + public int getIndex() { + return index; + } + + public String getId() { + return key.getIndex()+":"+getIndex(); + } + + @Override + public String toString() { + return description; + } + } + + public static final Key KEY_EXPORT = new Key(0, "Vector export mode"); + public static final Object VALUE_EXPORT_READABILITY = new Value(KEY_EXPORT, 0, "Maximize readability for humans"); + public static final Object VALUE_EXPORT_QUALITY = new Value(KEY_EXPORT, 1, "Maximize render quality"); + public static final Object VALUE_EXPORT_SIZE = new Value(KEY_EXPORT, 2, "Minimize data size"); + + public static final Key KEY_TEXT = new Key(1, "Text export mode"); + public static final Object VALUE_TEXT_DEFAULT = new Value(KEY_TEXT, 0, "Keep text"); + public static final Object VALUE_TEXT_VECTOR = new Value(KEY_TEXT, 1, "Convert text to vector shapes"); +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSDocument.java b/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSDocument.java new file mode 100644 index 00000000..249a0f70 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSDocument.java @@ -0,0 +1,515 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.eps; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.color.ColorSpace; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.erichseifert.vectorgraphics2d.GraphicsState; +import de.erichseifert.vectorgraphics2d.SizedDocument; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.CreateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawImageCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawStringCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.FillShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.RotateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.ScaleCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetClipCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetColorCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetCompositeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetFontCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetPaintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetStrokeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.ShearCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.TransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.TranslateCommand; +import de.erichseifert.vectorgraphics2d.util.ASCII85EncodeStream; +import de.erichseifert.vectorgraphics2d.util.AlphaToMaskOp; +import de.erichseifert.vectorgraphics2d.util.DataUtils; +import de.erichseifert.vectorgraphics2d.util.FlateEncodeStream; +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; +import de.erichseifert.vectorgraphics2d.util.ImageDataStream; +import de.erichseifert.vectorgraphics2d.util.ImageDataStream.Interleaving; +import de.erichseifert.vectorgraphics2d.util.LineWrapOutputStream; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * Represents a {@code Document} in the Encapsulated PostScript® + * (EPS) format. + */ +class EPSDocument extends SizedDocument { + /** Constant to convert values from millimeters to PostScript® units + (1/72th inch). */ + private static final double UNITS_PER_MM = 72.0 / 25.4; + private static final String CHARSET = "ISO-8859-1"; + private static final String EOL = "\n"; + private static final int MAX_LINE_WIDTH = 255; + private static final Pattern ELEMENT_SEPARATION_PATTERN = Pattern.compile("(.{1," + MAX_LINE_WIDTH + "})(\\s+|$)"); + + /** Mapping of stroke endcap values from Java to PostScript®. */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[] { BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE }, + new Integer[] { 0, 1, 2 } + ); + + /** Mapping of line join values for path drawing from Java to + PostScript®. */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[] { BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL }, + new Integer[] { 0, 1, 2 } + ); + + private static final String FONT_LATIN1_SUFFIX = "Lat"; + + private final List elements; + + public EPSDocument(CommandSequence commands, PageSize pageSize) { + super(pageSize, true); + elements = new LinkedList<>(); + addHeader(); + for (Command command : commands) { + handle(command); + } + } + + private void addHeader() { + double x=getPageSize().getX()*UNITS_PER_MM, + y=getPageSize().getY()*UNITS_PER_MM, + width=getPageSize().getWidth()*UNITS_PER_MM, + height=getPageSize().getHeight()*UNITS_PER_MM; + elements.addAll(Arrays.asList( + "%!PS-Adobe-3.0 EPSF-3.0", + "%%BoundingBox: " + ((int) Math.floor(x)) + " " + ((int) Math.floor(y)) + " " + ((int) Math.ceil(x + width)) + " " + ((int) Math.ceil(y + height)), + "%%HiResBoundingBox: " + x + " " + y + " " + (x + width) + " " + (y + height), + "%%LanguageLevel: 3", + "%%Pages: 1", + "%%EndComments", + "%%Page: 1 1", + "/M /moveto load def", + "/L /lineto load def", + "/C /curveto load def", + "/Z /closepath load def", + "/RL /rlineto load def", + "/rgb /setrgbcolor load def", + "/cmyk /setcmykcolor load def", + "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def", + "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def", + "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] /ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def", + "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def", + getOutput(GraphicsState.DEFAULT_FONT), + "gsave", + "clipsave", + "/DeviceRGB setcolorspace", + "0 " + height + " translate", + UNITS_PER_MM + " " + (-UNITS_PER_MM) + " scale", + "/basematrix matrix currentmatrix def" + )); + } + + public void writeTo(OutputStream out) throws IOException { + OutputStreamWriter o = new OutputStreamWriter(out, CHARSET); + for (String element : elements) { + if (element == null) { + continue; + } + + // Write current element in lines of 255 bytes (excluding line terminators) + // Numbers must not be separated by line breaks or errors will occur + // TODO: Integrate functionality into LineWrapOutputStream + Matcher chunkMatcher = ELEMENT_SEPARATION_PATTERN.matcher(element); + + boolean chunkFound = false; + while (chunkMatcher.find()) { + chunkFound = true; + String chunk = chunkMatcher.group(); + o.write(chunk, 0, chunk.length()); + o.append(EOL); + } + if (!chunkFound) { + // TODO: Exception, if no whitespace can be found in the chunk + System.err.println("Unable to divide eps element into lines: " + element); + } + } + o.append("%%EOF"); + o.flush(); + } + + public void handle(Command command) { + if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + Shape clip = c.getValue(); + elements.add("cliprestore"); + if (clip != null) { + elements.add(getOutput(clip) + " clip"); + } + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + // TODO Implement composite rendering for EPS + elements.add("% composite not yet implemented: " + c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + // TODO Implement paint rendering for EPS + elements.add("% paint not yet implemented: " + c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + elements.add(getOutput(c.getValue())); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("basematrix setmatrix [") + .append(DataUtils.join(" ", matrix)).append("] concat"); + elements.add(e.toString()); + } else if (command instanceof RotateCommand) { + RotateCommand c = (RotateCommand) command; + StringBuilder e = new StringBuilder(); + double x = c.getCenterX(); + double y = c.getCenterY(); + boolean translated = x != 0.0 || y != 0.0; + if (translated) { + e.append(x).append(" ").append(y).append(" translate "); + } + e.append(Math.toDegrees(c.getTheta())).append(" rotate"); + if (translated) { + e.append(" "); + e.append(-x).append(" ").append(-y).append(" translate"); + } + elements.add(e.toString()); + } else if (command instanceof ScaleCommand) { + ScaleCommand c = (ScaleCommand) command; + elements.add(DataUtils.format(c.getScaleX()) + " " + DataUtils.format(c.getScaleY()) + " scale"); + } else if (command instanceof ShearCommand) { + ShearCommand c = (ShearCommand) command; + elements.add("[1 " + DataUtils.format(c.getShearY()) + " " + DataUtils.format(c.getShearX()) + " 1 0 0] concat"); + } else if (command instanceof TransformCommand) { + TransformCommand c = (TransformCommand) command; + StringBuilder e = new StringBuilder(); + double[] matrix = new double[6]; + c.getValue().getMatrix(matrix); + e.append("[").append(DataUtils.join(" ", matrix)) + .append("] concat"); + elements.add(e.toString()); + } else if (command instanceof TranslateCommand) { + TranslateCommand c = (TranslateCommand) command; + elements.add(String.valueOf(c.getDeltaX()) + " " + c.getDeltaY() + " translate"); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + String e = getOutput(c.getValue(), + c.getImageWidth(), c.getImageHeight(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + elements.add(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + elements.add(getOutput(c.getValue()) + " stroke"); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + elements.add(getOutput(c.getValue(), c.getX(), c.getY())); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + String fillMethod = " fill"; + Shape shape = c.getValue(); + if (shape instanceof Path2D && ((Path2D) shape).getWindingRule() == Path2D.WIND_EVEN_ODD) { + fillMethod = " eofill"; + } + elements.add(getOutput(c.getValue()) + fillMethod); + } else if (command instanceof CreateCommand) { + elements.add("gsave"); + } else if (command instanceof DisposeCommand) { + elements.add("grestore"); + } + } + + private static String getOutput(Color color) { + // TODO Handle transparency + if (color.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + float[] cmyk = color.getComponents(null); + return String.format((Locale) null, "%f %f %f %f cmyk", + cmyk[0], cmyk[1], cmyk[2], cmyk[3]); + } else { + return String.format((Locale) null, "%f %f %f rgb", + color.getRed()/255f, color.getGreen()/255f, color.getBlue()/255f); + } + } + + private static String getOutput(Shape s) { + StringBuilder out = new StringBuilder(); + out.append("newpath "); + if (s instanceof Line2D) { + Line2D l = (Line2D) s; + out.append(l.getX1()).append(" ").append(l.getY1()).append(" M ") + .append(l.getX2()).append(" ").append(l.getY2()).append(" L"); + } else if (s instanceof Rectangle2D) { + Rectangle2D r = (Rectangle2D) s; + out.append(r.getX()).append(" ").append(r.getY()).append(" ") + .append(r.getWidth()).append(" ").append(r.getHeight()) + .append(" rect Z"); + } else if (s instanceof Ellipse2D) { + Ellipse2D e = (Ellipse2D) s; + double x = e.getX() + e.getWidth()/2.0; + double y = e.getY() + e.getHeight()/2.0; + double rx = e.getWidth()/2.0; + double ry = e.getHeight()/2.0; + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(360.0).append(" ").append(0.0) + .append(" ellipse Z"); + } else if (s instanceof Arc2D) { + Arc2D e = (Arc2D) s; + double x = (e.getX() + e.getWidth()/2.0); + double y = (e.getY() + e.getHeight()/2.0); + double rx = e.getWidth()/2.0; + double ry = e.getHeight()/2.0; + double startAngle = -e.getAngleStart(); + double endAngle = -(e.getAngleStart() + e.getAngleExtent()); + out.append(x).append(" ").append(y).append(" ") + .append(rx).append(" ").append(ry).append(" ") + .append(startAngle).append(" ").append(endAngle) + .append(" ellipse"); + if (e.getArcType() == Arc2D.CHORD) { + out.append(" Z"); + } else if (e.getArcType() == Arc2D.PIE) { + out.append(" ").append(x).append(" ").append(y).append(" L Z"); + } + } else { + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" M"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" L"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + out.append(coordsCur[0]).append(" ").append(coordsCur[1]) + .append(" ").append(coordsCur[2]).append(" ") + .append(coordsCur[3]).append(" ").append(coordsCur[4]) + .append(" ").append(coordsCur[5]).append(" C"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0/3.0*(coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0/3.0*(coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0/3.0*(coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0/3.0*(coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + out.append(x1).append(" ").append(y1).append(" ") + .append(x2).append(" ").append(y2).append(" ") + .append(x3).append(" ").append(y3).append(" C"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + } + return out.toString(); + } + + private static String getOutput(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) { + StringBuilder out = new StringBuilder(); + + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + int bands = bufferedImage.getSampleModel().getNumBands(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + bitsPerSample = (int) (Math.ceil(bitsPerSample/8.0)*8.0); + if (bands > 3) { + bands = 3; + } + + out.append("gsave").append(EOL); + if (x != 0.0 || y != 0.0) { + out.append(x).append(" ").append(y).append(" translate").append(EOL); + } + if (width != 1.0 || height != 1.0) { + out.append(width).append(" ").append(height).append(" scale").append(EOL); + } + + int decodeScale = 1; + if (bufferedImage.getColorModel().hasAlpha()) { + // TODO Use different InterleaveType (2 or 3) for more efficient compression + out.append("<< /ImageType 3 /InterleaveType 1 ") + .append("/MaskDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(1).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(false).append(" ").append(0).append(" imgdict ") + .append("/DataDict ") + .append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append(">> image").append(EOL); + + // Convert alpha values to binary mask + // FIXME Do alpha conversion in a preprocessing step on commands + bufferedImage = new AlphaToMaskOp(true).filter(bufferedImage, null); + output(bufferedImage, out); + } else { + if (bands == 1) { + out.append("/DeviceGray setcolorspace").append(EOL); + } + if (bufferedImage.getType() == BufferedImage.TYPE_BYTE_BINARY) { + decodeScale = 255; + } + out.append(imageWidth).append(" ").append(imageHeight).append(" ") + .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ") + .append(true).append(" currentfile /ASCII85Decode filter ") + .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ") + .append("/FlateDecode filter ") + .append("imgdict ") + .append("image").append(EOL); + output(bufferedImage, out); + } + + out.append("grestore"); + return out.toString(); + } + + private static void output(BufferedImage image, StringBuilder out) { + InputStream imageDataStream = + new ImageDataStream(image, Interleaving.SAMPLE); + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + OutputStream compressionStream = new FlateEncodeStream( + new ASCII85EncodeStream( + new LineWrapOutputStream(outBytes, 80))); + try { + DataUtils.transfer(imageDataStream, compressionStream, 1024); + compressionStream.close(); + String compressed = outBytes.toString(CHARSET); + out.append(compressed).append(EOL); + } catch (IOException e) { + // TODO Handle exception + e.printStackTrace(); + } + } + + private static String getOutput(String str, double x, double y) { + + return "gsave 1 -1 scale " + x + " " + -y + " M " + getOutput(str) + " show " + "grestore"; + } + + private static StringBuilder getOutput(String str) { + StringBuilder out = new StringBuilder(); + + // Escape text + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + + out.append("(").append(str).append(")"); + + return out; + } + + private static String getOutput(Stroke s) { + StringBuilder out = new StringBuilder(); + if (s instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) s; + out.append(bs.getLineWidth()).append(" setlinewidth ") + .append(STROKE_LINEJOIN.get(bs.getLineJoin())).append(" setlinejoin ") + .append(STROKE_ENDCAPS.get(bs.getEndCap())).append(" setlinecap ") + .append("[").append(DataUtils.join(" ", bs.getDashArray())).append("] ") + .append(bs.getDashPhase()).append(" setdash"); + } else { + out.append("% Custom strokes aren't supported at the moment"); + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + font = GraphicsUtils.getPhysicalFont(font); + String fontName = font.getPSName(); + + // Convert font to ISO-8859-1 encoding + String fontNameLatin1 = fontName + FONT_LATIN1_SUFFIX; + out.append("/").append(fontNameLatin1).append(" ") + .append("/").append(font.getPSName()).append(" latinize "); + + // Use encoded font + out.append("/").append(fontNameLatin1).append(" ") + .append(font.getSize2D()).append(" selectfont"); + + return out.toString(); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSProcessor.java b/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSProcessor.java new file mode 100644 index 00000000..9653296b --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/eps/EPSProcessor.java @@ -0,0 +1,48 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.eps; + +import de.erichseifert.vectorgraphics2d.Document; +import de.erichseifert.vectorgraphics2d.Processor; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.filters.FillPaintedShapeAsImageFilter; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * {@code Processor} implementation that translates {@link CommandSequence}s to + * a {@code Document} in the Encapsulated PostScript® (EPS) format. + */ +public class EPSProcessor implements Processor { + /** + * Initializes an {@code EPSProcessor}. + */ + public EPSProcessor() {} + + @Override + public Document getDocument(CommandSequence commands, PageSize pageSize) { + // TODO Apply rotate(theta,x,y) => translate-rotate-translate filter + // TODO Apply image transparency => image mask filter + // TODO Apply optimization filter + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(commands); + return new EPSDocument(paintedShapeAsImageFilter, pageSize); + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/eps/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/eps/package-info.java new file mode 100644 index 00000000..7fb1c10c --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/eps/package-info.java @@ -0,0 +1,26 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Implementation of a processor to convert VectorGraphics2D intermediate + * language commands to Encapsulated PostScript® (EPS). + */ +package de.erichseifert.vectorgraphics2d.eps; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/CommandSequence.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/CommandSequence.java new file mode 100644 index 00000000..bf740fec --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/CommandSequence.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate; + +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; + +/** + * Represents a sequence of {@link Command} objects. + * The individual {@code Command}s can be retrieved through an {@code Iterator}. + */ +public interface CommandSequence extends Iterable> { +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/MutableCommandSequence.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/MutableCommandSequence.java new file mode 100644 index 00000000..9321fa1d --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/MutableCommandSequence.java @@ -0,0 +1,52 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; + +/** + * Mutable implementation of {@code CommandSequence}. + * It is possible to add commands to this sequence. + */ +public class MutableCommandSequence implements CommandSequence { + private final List> commands; + + /** + * Initializes a new {@code CommandSequence} object. + */ + public MutableCommandSequence() { + this.commands = new LinkedList<>(); + } + + public void add(Command command) { + commands.add(command); + } + + @Override + public Iterator> iterator() { + return commands.iterator(); + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/AffineTransformCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/AffineTransformCommand.java new file mode 100644 index 00000000..8c647740 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/AffineTransformCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public abstract class AffineTransformCommand extends StateCommand { + public AffineTransformCommand(AffineTransform transform) { + super(transform); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Command.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Command.java new file mode 100644 index 00000000..c6437e5e --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Command.java @@ -0,0 +1,52 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.util.Locale; + +public abstract class Command { + private final T value; + + public Command(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + + @Override + public String toString() { + return String.format((Locale) null, "%s[value=%s]", + getClass().getName(), getValue()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } + Command o = (Command) obj; + return value == o.value || value.equals(o.value); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/CreateCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/CreateCommand.java new file mode 100644 index 00000000..bfb1c894 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/CreateCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import de.erichseifert.vectorgraphics2d.VectorGraphics2D; + +public class CreateCommand extends StateCommand { + public CreateCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DisposeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DisposeCommand.java new file mode 100644 index 00000000..72e2e51f --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DisposeCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import de.erichseifert.vectorgraphics2d.VectorGraphics2D; + +public class DisposeCommand extends StateCommand { + public DisposeCommand(VectorGraphics2D graphics) { + super(graphics); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawImageCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawImageCommand.java new file mode 100644 index 00000000..bcc21962 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawImageCommand.java @@ -0,0 +1,75 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Image; +import java.util.Locale; + +public class DrawImageCommand extends Command { + private final int imageWidth; + private final int imageHeight; + private final double x; + private final double y; + private final double width; + private final double height; + + public DrawImageCommand(Image image, int imageWidth, int imageHeight, + double x, double y, double width, double height) { + super(image); + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public int getImageWidth() { + return imageWidth; + } + public int getImageHeight() { + return imageHeight; + } + + public double getX() { + return x; + } + public double getY() { + return y; + } + public double getWidth() { + return width; + } + public double getHeight() { + return height; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[value=%s, imageWidth=%d, imageHeight=%d, x=%f, y=%f, width=%f, height=%f]", + getClass().getName(), getValue(), + getImageWidth(), getImageHeight(), + getX(), getY(), getWidth(), getHeight()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawShapeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawShapeCommand.java new file mode 100644 index 00000000..552f6c8f --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawShapeCommand.java @@ -0,0 +1,33 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Shape; + +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; + +public class DrawShapeCommand extends Command { + public DrawShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawStringCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawStringCommand.java new file mode 100644 index 00000000..969edca2 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/DrawStringCommand.java @@ -0,0 +1,51 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.util.Locale; + + +public class DrawStringCommand extends Command { + private final double x; + private final double y; + + public DrawStringCommand(String string, double x, double y) { + super(string); + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + @Override + public String toString() { + return String.format((Locale) null, "%s[value=%s, x=%f, y=%f]", + getClass().getName(), getValue(), getX(), getY()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/FillShapeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/FillShapeCommand.java new file mode 100644 index 00000000..e44c084a --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/FillShapeCommand.java @@ -0,0 +1,33 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Shape; + +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; + +public class FillShapeCommand extends Command { + public FillShapeCommand(Shape shape) { + super(GraphicsUtils.clone(shape)); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Group.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Group.java new file mode 100644 index 00000000..42b1e1d9 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/Group.java @@ -0,0 +1,37 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.util.LinkedList; +import java.util.List; + +public class Group extends Command>> { + public Group() { + super(new LinkedList<>()); + } + + public void add(Command command) { + List> group = getValue(); + group.add(command); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/RotateCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/RotateCommand.java new file mode 100644 index 00000000..fe91c9f8 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/RotateCommand.java @@ -0,0 +1,59 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class RotateCommand extends AffineTransformCommand { + private final double theta; + private final double centerX; + private final double centerY; + + public RotateCommand(double theta, double centerX, double centerY) { + super(AffineTransform.getRotateInstance(theta, centerX, centerY)); + this.theta = theta; + this.centerX = centerX; + this.centerY = centerY; + } + + public double getTheta() { + return theta; + } + + public double getCenterX() { + return centerX; + } + + public double getCenterY() { + return centerY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[theta=%f, centerX=%f, centerY=%f, value=%s]", + getClass().getName(), getTheta(), getCenterX(), getCenterY(), + getValue()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ScaleCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ScaleCommand.java new file mode 100644 index 00000000..c88a31ca --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ScaleCommand.java @@ -0,0 +1,52 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ScaleCommand extends AffineTransformCommand { + private final double scaleX; + private final double scaleY; + + public ScaleCommand(double scaleX, double scaleY) { + super(AffineTransform.getScaleInstance(scaleX, scaleY)); + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + public double getScaleX() { + return scaleX; + } + + public double getScaleY() { + return scaleY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[scaleX=%f, scaleY=%f, value=%s]", getClass().getName(), + getScaleX(), getScaleY(), getValue()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetBackgroundCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetBackgroundCommand.java new file mode 100644 index 00000000..90188b09 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetBackgroundCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Color; + +public class SetBackgroundCommand extends StateCommand { + public SetBackgroundCommand(Color color) { + super(color); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetClipCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetClipCommand.java new file mode 100644 index 00000000..5b63ce10 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetClipCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Shape; + +public class SetClipCommand extends StateCommand { + public SetClipCommand(Shape shape) { + super(shape); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetColorCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetColorCommand.java new file mode 100644 index 00000000..997947f8 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetColorCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Color; + +public class SetColorCommand extends StateCommand { + public SetColorCommand(Color color) { + super(color); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetCompositeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetCompositeCommand.java new file mode 100644 index 00000000..89328cb2 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetCompositeCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Composite; + +public class SetCompositeCommand extends StateCommand { + public SetCompositeCommand(Composite composite) { + super(composite); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetFontCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetFontCommand.java new file mode 100644 index 00000000..01d82d8f --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetFontCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Font; + +public class SetFontCommand extends StateCommand { + public SetFontCommand(Font font) { + super(font); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetHintCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetHintCommand.java new file mode 100644 index 00000000..d9c7f914 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetHintCommand.java @@ -0,0 +1,45 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.util.Locale; + +public class SetHintCommand extends StateCommand { + private final Object key; + + public SetHintCommand(Object hintKey, Object hintValue) { + super(hintValue); + key = hintKey; + } + + public Object getKey() { + return key; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[key=%s, value=%s]", getClass().getName(), + getKey(), getValue()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetPaintCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetPaintCommand.java new file mode 100644 index 00000000..fce2bad8 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetPaintCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Paint; + +public class SetPaintCommand extends StateCommand { + public SetPaintCommand(Paint paint) { + super(paint); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetStrokeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetStrokeCommand.java new file mode 100644 index 00000000..7d2a3e75 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetStrokeCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Stroke; + +public class SetStrokeCommand extends StateCommand { + public SetStrokeCommand(Stroke stroke) { + super(stroke); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetTransformCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetTransformCommand.java new file mode 100644 index 00000000..6d95ee35 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetTransformCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public class SetTransformCommand extends StateCommand { + public SetTransformCommand(AffineTransform transform) { + super(new AffineTransform(transform)); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetXORModeCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetXORModeCommand.java new file mode 100644 index 00000000..e11476be --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/SetXORModeCommand.java @@ -0,0 +1,31 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.Color; + +public class SetXORModeCommand extends StateCommand { + public SetXORModeCommand(Color mode) { + super(mode); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ShearCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ShearCommand.java new file mode 100644 index 00000000..db7865ab --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/ShearCommand.java @@ -0,0 +1,52 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class ShearCommand extends AffineTransformCommand { + private final double shearX; + private final double shearY; + + public ShearCommand(double shearX, double shearY) { + super(AffineTransform.getShearInstance(shearX, shearY)); + this.shearX = shearX; + this.shearY = shearY; + } + + public double getShearX() { + return shearX; + } + + public double getShearY() { + return shearY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[shearX=%f, shearY=%f, value=%s]", getClass().getName(), + getShearX(), getShearY(), getValue()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/StateCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/StateCommand.java new file mode 100644 index 00000000..0cff6806 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/StateCommand.java @@ -0,0 +1,29 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +public abstract class StateCommand extends Command { + public StateCommand(T value) { + super(value); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TransformCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TransformCommand.java new file mode 100644 index 00000000..ad7e9356 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TransformCommand.java @@ -0,0 +1,38 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; + +public class TransformCommand extends AffineTransformCommand { + private final AffineTransform transform; + + public TransformCommand(AffineTransform transform) { + super(transform); + this.transform = new AffineTransform(transform); + } + + public AffineTransform getTransform() { + return transform; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TranslateCommand.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TranslateCommand.java new file mode 100644 index 00000000..5d102ed4 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/TranslateCommand.java @@ -0,0 +1,52 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; + +import java.awt.geom.AffineTransform; +import java.util.Locale; + +public class TranslateCommand extends AffineTransformCommand { + private final double deltaX; + private final double deltaY; + + public TranslateCommand(double x, double y) { + super(AffineTransform.getTranslateInstance(x, y)); + this.deltaX = x; + this.deltaY = y; + } + + public double getDeltaX() { + return deltaX; + } + + public double getDeltaY() { + return deltaY; + } + + @Override + public String toString() { + return String.format((Locale) null, + "%s[deltaX=%f, deltaY=%f, value=%s]", getClass().getName(), + getDeltaX(), getDeltaY(), getValue()); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/package-info.java new file mode 100644 index 00000000..d68070ca --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/commands/package-info.java @@ -0,0 +1,26 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Commands of the VectorGraphics2D intermediate language that describe calls + * to the Graphics2D interface of Java. + */ +package de.erichseifert.vectorgraphics2d.intermediate.commands; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/AbsoluteToRelativeTransformsFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/AbsoluteToRelativeTransformsFilter.java new file mode 100644 index 00000000..c1326352 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/AbsoluteToRelativeTransformsFilter.java @@ -0,0 +1,85 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.util.Collections; +import java.util.List; +import java.util.Stack; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.AffineTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.CreateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.TransformCommand; + +public class AbsoluteToRelativeTransformsFilter extends StreamingFilter { + private final Stack transforms; + + public AbsoluteToRelativeTransformsFilter(CommandSequence stream) { + super(stream); + transforms = new Stack<>(); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + if (nextCommand instanceof AffineTransformCommand) { + AffineTransformCommand affineTransformCommand = (AffineTransformCommand) nextCommand; + getCurrentTransform().concatenate(affineTransformCommand.getValue()); + } else if (nextCommand instanceof CreateCommand) { + AffineTransform newTransform = transforms.isEmpty() ? new AffineTransform() : new AffineTransform(getCurrentTransform()); + transforms.push(newTransform); + } else if (nextCommand instanceof DisposeCommand) { + transforms.pop(); + } + + return nextCommand; + } + + @Override + protected List> filter(Command command) { + if (command instanceof SetTransformCommand) { + SetTransformCommand setTransformCommand = (SetTransformCommand) command; + AffineTransform absoluteTransform = setTransformCommand.getValue(); + AffineTransform relativeTransform = new AffineTransform(); + try { + AffineTransform invertedOldTransformation = getCurrentTransform().createInverse(); + relativeTransform.concatenate(invertedOldTransformation); + } catch (NoninvertibleTransformException e) { + e.printStackTrace(); + } + relativeTransform.concatenate(absoluteTransform); + TransformCommand transformCommand = new TransformCommand(relativeTransform); + return Collections.singletonList(transformCommand); + } + return Collections.singletonList(command); + } + + private AffineTransform getCurrentTransform() { + return transforms.peek(); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/FillPaintedShapeAsImageFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/FillPaintedShapeAsImageFilter.java new file mode 100644 index 00000000..f5a099d5 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/FillPaintedShapeAsImageFilter.java @@ -0,0 +1,91 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.Collections; +import java.util.List; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawImageCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.FillShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetPaintCommand; + +public class FillPaintedShapeAsImageFilter extends StreamingFilter { + private SetPaintCommand lastSetPaintCommand; + + public FillPaintedShapeAsImageFilter(CommandSequence stream) { + super(stream); + } + + @Override + public Command next() { + Command nextCommand = super.next(); + + if (nextCommand instanceof SetPaintCommand) { + lastSetPaintCommand = (SetPaintCommand) nextCommand; + } else if (nextCommand instanceof DisposeCommand) { + lastSetPaintCommand = null; + } + + return nextCommand; + } + + private DrawImageCommand getDrawImageCommand(FillShapeCommand shapeCommand, SetPaintCommand paintCommand) { + Shape shape = shapeCommand.getValue(); + Rectangle2D shapeBounds = shape.getBounds2D(); + double x = shapeBounds.getX(); + double y = shapeBounds.getY(); + double width = shapeBounds.getWidth(); + double height = shapeBounds.getHeight(); + int imageWidth = (int) Math.round(width); + int imageHeight = (int) Math.round(height); + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D imageGraphics = (Graphics2D) image.getGraphics(); + imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + imageGraphics.scale(imageWidth/width, imageHeight/height); + imageGraphics.translate(-shapeBounds.getX(), -shapeBounds.getY()); + imageGraphics.setPaint(paintCommand.getValue()); + imageGraphics.fill(shape); + imageGraphics.dispose(); + + return new DrawImageCommand(image, imageWidth, imageHeight, x, y, width, height); + } + + @Override + protected List> filter(Command command) { + if (lastSetPaintCommand != null && command instanceof FillShapeCommand) { + FillShapeCommand fillShapeCommand = (FillShapeCommand) command; + DrawImageCommand drawImageCommand = getDrawImageCommand(fillShapeCommand, lastSetPaintCommand); + return Collections.singletonList(drawImageCommand); + } + + return Collections.singletonList(command); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/Filter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/Filter.java new file mode 100644 index 00000000..9ec1db2e --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/Filter.java @@ -0,0 +1,27 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; + +public interface Filter extends CommandSequence { +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/GroupingFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/GroupingFilter.java new file mode 100644 index 00000000..85f65606 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/GroupingFilter.java @@ -0,0 +1,69 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import java.util.Collections; +import java.util.List; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Group; + + +public abstract class GroupingFilter extends StreamingFilter { + private Group group; + + public GroupingFilter(CommandSequence stream) { + super(stream); + } + + @Override + public boolean hasNext() { + return group != null || super.hasNext(); + } + + @Override + public Command next() { + if (group == null) { + return super.next(); + } + Group g = group; + group = null; + return g; + } + + @Override + protected List> filter(Command command) { + boolean grouped = isGrouped(command); + if (grouped) { + if (group == null) { + group = new Group(); + } + group.add(command); + return null; + } + return Collections.singletonList(command); + } + + protected abstract boolean isGrouped(Command command); +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/OptimizeFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/OptimizeFilter.java new file mode 100644 index 00000000..a264d7e8 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/OptimizeFilter.java @@ -0,0 +1,79 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.AffineTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetHintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.StateCommand; + +public class OptimizeFilter extends StreamingFilter { + private final Queue> buffer; + + public OptimizeFilter(CommandSequence stream) { + super(stream); + buffer = new LinkedList<>(); + } + + @Override + public boolean hasNext() { + return super.hasNext(); + } + + @Override + public Command next() { + if (buffer.isEmpty()) { + return super.next(); + } + return buffer.poll(); + } + + @Override + protected List> filter(Command command) { + if (!isStateChange(command)) { + return Collections.singletonList(command); + } + Iterator> i = buffer.iterator(); + Class cls = command.getClass(); + while (i.hasNext()) { + if (cls.equals(i.next().getClass())) { + i.remove(); + } + } + buffer.add(command); + return null; + } + + private static boolean isStateChange(Command command) { + return (command instanceof StateCommand) && + !(command instanceof AffineTransformCommand) && + !(command instanceof SetHintCommand); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StateChangeGroupingFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StateChangeGroupingFilter.java new file mode 100644 index 00000000..1a0db512 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StateChangeGroupingFilter.java @@ -0,0 +1,40 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.StateCommand; + + +public class StateChangeGroupingFilter extends GroupingFilter { + + public StateChangeGroupingFilter(CommandSequence stream) { + super(stream); + } + + @Override + protected boolean isGrouped(Command command) { + return command instanceof StateCommand; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StreamingFilter.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StreamingFilter.java new file mode 100644 index 00000000..8e950355 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/StreamingFilter.java @@ -0,0 +1,70 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; + +public abstract class StreamingFilter implements Iterator>, Filter { + private final Queue> buffer; + private final Iterator> iterator; + + public StreamingFilter(CommandSequence stream) { + buffer = new LinkedList<>(); + iterator = stream.iterator(); + } + + public Iterator> iterator() { + return this; + } + + public boolean hasNext() { + findNextCommand(); + return !buffer.isEmpty(); + } + + private void findNextCommand() { + while (buffer.isEmpty() && iterator.hasNext()) { + Command command = iterator.next(); + List> commands = filter(command); + if (commands != null) { + buffer.addAll(commands); + } + } + } + + public Command next() { + findNextCommand(); + return buffer.poll(); + } + + public void remove() { + } + + protected abstract List> filter(Command command); +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/package-info.java new file mode 100644 index 00000000..22bfc3b3 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/filters/package-info.java @@ -0,0 +1,26 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Classes that filter or alter a sequence of commands in the VectorGraphics2D + * intermediate language. + */ +package de.erichseifert.vectorgraphics2d.intermediate.filters; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/intermediate/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/package-info.java new file mode 100644 index 00000000..9bb451fd --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/intermediate/package-info.java @@ -0,0 +1,27 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Classes for the VectorGraphics2D intermediate language. + * It describes all calls to the Graphics2D interface of Java using commands + * and filters. + */ +package de.erichseifert.vectorgraphics2d.intermediate; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/package-info.java new file mode 100644 index 00000000..ace1e6a6 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/package-info.java @@ -0,0 +1,25 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Main classes. + */ +package de.erichseifert.vectorgraphics2d; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/DefaultPDFObject.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/DefaultPDFObject.java new file mode 100644 index 00000000..5f1cf391 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/DefaultPDFObject.java @@ -0,0 +1,45 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.util.LinkedHashMap; +import java.util.Map; + +class DefaultPDFObject implements PDFObject { + public final Map dict; + public final Payload payload; + public final boolean stream; + + public DefaultPDFObject(Map dict, Payload payload, boolean stream) { + this.dict = new LinkedHashMap<>(); + this.payload = payload; + this.stream = stream; + if (dict != null) { + this.dict.putAll(dict); + } + } + + public String getType() { + return (String) dict.get("Type"); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFDocument.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFDocument.java new file mode 100644 index 00000000..e00ffd2c --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFDocument.java @@ -0,0 +1,835 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import de.erichseifert.vectorgraphics2d.GraphicsState; +import de.erichseifert.vectorgraphics2d.SizedDocument; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.AffineTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.CreateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawImageCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawStringCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.FillShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Group; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetBackgroundCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetClipCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetColorCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetFontCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetHintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetPaintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetStrokeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetTransformCommand; +import de.erichseifert.vectorgraphics2d.util.DataUtils; +import de.erichseifert.vectorgraphics2d.util.FlateEncodeStream; +import de.erichseifert.vectorgraphics2d.util.FormattingWriter; +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; +import de.erichseifert.vectorgraphics2d.util.ImageDataStream; +import de.erichseifert.vectorgraphics2d.util.ImageDataStream.Interleaving; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * Represents a {@code Document} in the Portable Document Format (PDF). + */ +// TODO Support for different image formats (binary, grayscale, etc.) +class PDFDocument extends SizedDocument { + private static final String CHARSET = "ISO-8859-1"; + private static final String HEADER = "%PDF-1.4"; + private static final String FOOTER = "%%EOF"; + private static final String EOL = "\n"; + + /** Constant to convert values from millimeters to PostScript®/PDF units (1/72th inch). */ + private static final double MM_IN_UNITS = 72.0/25.4; + + /** Mapping of stroke endcap values from Java to PDF. */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[] { BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE }, + new Integer[] { 0, 1, 2 } + ); + + /** Mapping of line join values for path drawing from Java to PDF. */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[] { BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL }, + new Integer[] { 0, 1, 2 } + ); + + private final List objects; + /** Cross-reference table ("xref"). */ + private final Map crossReferences; + + private final Stream contents; + private Resources resources; + private final Map images; + + private final Stack states; + private boolean transformed; + + PDFDocument(CommandSequence commands, PageSize pageSize, boolean compressed) { + super(pageSize, compressed); + + states = new Stack<>(); + states.push(new GraphicsState()); + + objects = new LinkedList<>(); + crossReferences = new HashMap<>(); + images = new HashMap<>(); + + contents = initPage(); + for (Command command : commands) { + try { + byte[] pdfStatement = toBytes(command); + contents.write(pdfStatement); + contents.write(EOL.getBytes(CHARSET)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + close(); + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + /** + * Initializes the document and returns a {@code Stream} representing the contents. + * @return {@code Stream} to which the contents are written. + */ + private Stream initPage() { + DefaultPDFObject catalog = addCatalog(); + + List pagesKids = new LinkedList<>(); + PDFObject pageTree = addPageTree(catalog, pagesKids); + + // Page + DefaultPDFObject page = addPage(pageTree); + pagesKids.add(page); + + // Contents + Stream.Filter[] filters = isCompressed() ? new Stream.Filter[] {Stream.Filter.FLATE} : new Stream.Filter[0]; + Stream contents = new Stream(filters); + objects.add(contents); + page.dict.put("Contents", contents); + + // Initial content + try { + double scaleH = MM_IN_UNITS; + double scaleV = -MM_IN_UNITS; + + PageSize pageSize = getPageSize(); + double translateX = -pageSize.getX()*MM_IN_UNITS; + double translateY = (pageSize.getY() + pageSize.getHeight())*MM_IN_UNITS; + + FormattingWriter string = new FormattingWriter(contents, CHARSET, EOL); + string.writeln("q"); + string.writeln(getOutput(getCurrentState().getColor())); + string.write(scaleH).write(" ") + .write(0.0).write(" ") + .write(0.0).write(" ") + .write(scaleV).write(" ") + .write(translateX).write(" ") + .write(translateY).writeln(" cm"); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Resources + resources = new Resources(); + objects.add(resources); + page.dict.put("Resources", resources); + + // Create initial font + Font font = getCurrentState().getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + setFont(fontResourceId, fontSize, contents); + return contents; + } + + private void setFont(String fontId, float fontSize, Stream contents) { + try { + FormattingWriter string = new FormattingWriter(contents, CHARSET, EOL); + string.write("/").write(fontId).write(" ").write(fontSize).writeln(" Tf"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private DefaultPDFObject addObject(Map dict, Payload payload) { + DefaultPDFObject object = new DefaultPDFObject(dict, payload, true); + objects.add(object); + return object; + } + + private DefaultPDFObject addCatalog() { + Map dict = DataUtils.map( + new String[] {"Type"}, + new Object[] {"Catalog"} + ); + return addDictionary(dict); + } + + private PDFObject addPageTree(DefaultPDFObject catalog, List pages) { + Map dict = DataUtils.map( + new String[] {"Type", "Kids", "Count"}, + new Object[] {"Pages", pages, 1} + ); + PDFObject pageTree = addDictionary(dict); + catalog.dict.put("Pages", pageTree); + return pageTree; + } + + private DefaultPDFObject addPage(PDFObject pageTree) { + double x = 0.0; + double y = 0.0; + double width = getPageSize().getWidth()*MM_IN_UNITS; + double height = getPageSize().getHeight()*MM_IN_UNITS; + Map dict = DataUtils.map( + new String[] {"Type", "Parent", "MediaBox"}, + new Object[] {"Page", pageTree, new double[] {x, y, width, height}} + ); + return addDictionary(dict); + } + + private DefaultPDFObject addDictionary(Map dict) { + DefaultPDFObject object = new DefaultPDFObject(dict, null, false); + objects.add(object); + return object; + } + + private DefaultPDFObject addObject(Image image) { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize()); + int bands = bufferedImage.getSampleModel().getNumBands(); + String colorSpaceName = (bands == 1) ? "DeviceGray" : "DeviceRGB"; + + Payload imagePayload = new Payload(); + + // Compression + String[] imageFilters = {}; + if (isCompressed()) { + imagePayload.addFilter(FlateEncodeStream.class); + imageFilters = new String[] {"FlateDecode"}; + } + + InputStream imageDataStream = + new ImageDataStream(bufferedImage, Interleaving.WITHOUT_ALPHA); + + try { + DataUtils.transfer(imageDataStream, imagePayload, 1024); + imagePayload.close(); + } catch (IOException e) { + // TODO Improve exception handling + throw new RuntimeException(e); + } + + int length = imagePayload.getBytes().length; + + Map imageDict = DataUtils.map( + new String[] {"Type", "Subtype", "Width", "Height", "ColorSpace", + "BitsPerComponent", "Length", "Filter"}, + new Object[] {"XObject", "Image", width, height, colorSpaceName, + bitsPerSample, length, imageFilters} + ); + + DefaultPDFObject imageObject = addObject(imageDict, imagePayload); + + boolean hasAlpha = bufferedImage.getColorModel().hasAlpha(); + if (hasAlpha) { + BufferedImage mask = GraphicsUtils.getAlphaImage(bufferedImage); + + DefaultPDFObject maskObject = addObject(mask); + + boolean isBitmask = mask.getSampleModel().getSampleSize(0) == 1; + if (isBitmask) { + maskObject.dict.put("ImageMask", true); + maskObject.dict.remove("ColorSpace"); + imageObject.dict.put("Mask", maskObject); + } else { + imageObject.dict.put("SMask", maskObject); + } + } + + return imageObject; + } + + public void writeTo(OutputStream out) throws IOException { + FormattingWriter o = new FormattingWriter(out, CHARSET, EOL); + + o.writeln(HEADER); + + for (PDFObject obj : objects) { + crossReferences.put(obj, o.tell()); + byte[] objectString; + if (obj instanceof Resources) { + objectString = toBytes((Resources) obj); + } else if (obj instanceof Stream) { + objectString = toBytes((Stream) obj); + } else { + objectString = toBytes(obj); + } + o.writeln(objectString); + o.flush(); + } + + long xrefPos = o.tell(); + o.writeln("xref"); + o.write(0).write(" ").writeln(objects.size() + 1); + o.writeln("%010d %05d f ", 0, 65535); + for (PDFObject obj : objects) { + o.writeln("%010d %05d n ", crossReferences.get(obj), 0); + } + o.flush(); + + o.writeln("trailer"); + o.writeln(serialize(DataUtils.map( + new String[] {"Size", "Root"}, + new Object[] {objects.size() + 1, objects.get(0)} + ))); + + o.writeln("startxref"); + o.writeln(xrefPos); + + o.writeln(FOOTER); + o.flush(); + } + + private int getId(PDFObject object) { + int index = objects.indexOf(object); + if (index < 0) { + throw new IllegalArgumentException("Object " + object + " is not part of this document."); + } + return index + 1; + } + + /** + * Returns the version of the specified object. + * @param object {@code PDFObject} whose version should be determined. + * @return Version number. + */ + private int getVersion(PDFObject object) { + return 0; + } + + private byte[] toBytes(Resources resources) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write(getId(resources)).write(" ").write(getVersion(resources)).writeln(" obj"); + string.writeln("<<"); + if (!resources.getProcSet().isEmpty()) { + string.write("/ProcSet ").writeln(serialize(resources.getProcSet())); + } + if (!resources.getFont().isEmpty()) { + string.write("/Font ").writeln(serialize(resources.getFont())); + } + if (resources.dict.get("ExtGState") != null) { + string.write("/ExtGState ").writeln(serialize(resources.dict.get("ExtGState"))); + } + if (resources.dict.get("XObject") != null) { + string.write("/XObject ").writeln(serialize(resources.dict.get("XObject"))); + } + string.writeln(">>"); + string.write("endobj"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] toBytes(Stream stream) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write(getId(stream)).write(" ").write(getVersion(stream)).writeln(" obj"); + string.writeln(serialize(stream)); + string.write("endobj"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + protected static byte[] serialize(Stream stream) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter serialized = new FormattingWriter(out, CHARSET, EOL)) { + serialized.writeln("<<"); + serialized.write("/Length ").writeln(stream.getLength()); + if (stream.getFilters().contains(Stream.Filter.FLATE)) { + serialized.writeln("/Filter /FlateDecode"); + } + serialized.writeln(">>"); + serialized.writeln("stream"); + serialized.writeln(stream.getContent()); + serialized.write("endstream"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + protected static byte[] serialize(TrueTypeFont font) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter serialized = new FormattingWriter(out, CHARSET, EOL)) { + serialized.writeln("<<"); + serialized.write("/Type /").writeln(font.getType()); + serialized.write("/Subtype /").writeln(font.getSubtype()); + serialized.write("/Encoding /").writeln(font.getEncoding()); + serialized.write("/BaseFont /").writeln(font.getBaseFont()); + serialized.write(">>"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] toBytes(PDFObject object) { + DefaultPDFObject obj = (DefaultPDFObject) object; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write(getId(obj)).write(" ").write(getVersion(obj)).writeln(" obj"); + if (!obj.dict.isEmpty()) { + string.writeln(serialize(obj.dict)); + } + if (obj.payload != null && !obj.payload.isEmpty()) { + if (obj.stream) { + string.writeln("stream"); + } + string.writeln(obj.payload.getBytes()); + if (obj.stream) { + string.writeln("endstream"); + } + } + string.write("endobj"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] serialize(Object obj) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter serialized = new FormattingWriter(out, CHARSET, EOL)) { + if (obj instanceof String) { + serialized.write("/").write(obj.toString()); + } else if (obj instanceof float[]) { + serialized.write(serialize(DataUtils.asList((float[]) obj))); + } else if (obj instanceof double[]) { + serialized.write(serialize(DataUtils.asList((double[]) obj))); + } else if (obj instanceof Object[]) { + serialized.write(serialize(Arrays.asList((Object[]) obj))); + } else if (obj instanceof List) { + List list = (List) obj; + serialized.write("["); + int i = 0; + for (Object elem : list) { + if (i++ > 0) { + serialized.write(" "); + } + serialized.write(serialize(elem)); + } + serialized.write("]"); + } else if (obj instanceof Map) { + Map dict = (Map) obj; + serialized.writeln("<<"); + for (Map.Entry entry : dict.entrySet()) { + String key = entry.getKey().toString(); + Object value = entry.getValue(); + serialized.write(serialize(key)).write(" ").writeln(serialize(value)); + } + serialized.write(">>"); + } else if (obj instanceof TrueTypeFont) { + serialized.write(serialize((TrueTypeFont) obj)); + } else if (obj instanceof PDFObject) { + PDFObject pdfObj = (PDFObject) obj; + serialized.write(getId(pdfObj)).write(" ").write(getVersion(pdfObj)).write(" R"); + } else { + serialized.write(DataUtils.format(obj)); + } + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] toBytes(Command command) { + byte[] s = {}; + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + s = getOutput(getCurrentState(), resources, !transformed); + transformed = true; + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + try (ByteArrayOutputStream ba = new ByteArrayOutputStream()) { + ba.write(getOutput(c.getValue())); + ba.write(serialize(" S")); + s = ba.toByteArray(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + String fillMethod = " f"; + Shape shape = c.getValue(); + if (shape instanceof Path2D && ((Path2D) shape).getWindingRule() == Path2D.WIND_EVEN_ODD) { + fillMethod = " f*"; + } + try (ByteArrayOutputStream ba = new ByteArrayOutputStream()) { + ba.write(getOutput(c.getValue())); + ba.write(serialize(fillMethod)); + s = ba.toByteArray(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + s = getOutput(c.getValue(), c.getX(), c.getY()); + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + // Create object for image data + Image image = c.getValue(); + PDFObject imageObject = images.get(image.hashCode()); + if (imageObject == null) { + imageObject = addObject(image); + images.put(image.hashCode(), imageObject); + } + s = getOutput(imageObject, c.getX(), c.getY(), + c.getWidth(), c.getHeight(), resources); + } + return s; + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + getCurrentState().getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + getCurrentState().setBackground(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + getCurrentState().setColor(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + getCurrentState().setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + getCurrentState().setStroke(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + getCurrentState().setFont(c.getValue()); + } else if (command instanceof SetTransformCommand) { + throw new UnsupportedOperationException("The PDF format has no means of setting the transformation matrix."); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = getCurrentState().getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + getCurrentState().setTransform(stateTransform); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + getCurrentState().setClip(c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + private byte[] getOutput(Color color) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + if (color.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + float[] cmyk = color.getComponents(null); + byte[] c = serialize(cmyk[0]); + byte[] m = serialize(cmyk[1]); + byte[] y = serialize(cmyk[2]); + byte[] k = serialize(cmyk[3]); + string.write(c).write(" ").write(m).write(" ").write(y).write(" ").write(k).write(" k "); + string.write(c).write(" ").write(m).write(" ").write(y).write(" ").write(k).write(" K"); + } else { + byte[] r = serialize(color.getRed()/255.0); + byte[] g = serialize(color.getGreen()/255.0); + byte[] b = serialize(color.getBlue()/255.0); + string.write(r).write(" ").write(g).write(" ").write(b).write(" rg "); + string.write(r).write(" ").write(g).write(" ").write(b).write(" RG"); + } + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] getOutput(Shape s) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + PathIterator segments = s.getPathIterator(null); + double[] coordsCur = new double[6]; + double[] pointPrev = new double[2]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + string.write(" "); + } + int segmentType = segments.currentSegment(coordsCur); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + string.write(serialize(coordsCur[0])).write(" ") + .write(serialize(coordsCur[1])).write(" m"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_LINETO: + string.write(serialize(coordsCur[0])).write(" ") + .write(serialize(coordsCur[1])).write(" l"); + pointPrev[0] = coordsCur[0]; + pointPrev[1] = coordsCur[1]; + break; + case PathIterator.SEG_CUBICTO: + string.write(serialize(coordsCur[0])).write(" ") + .write(serialize(coordsCur[1])).write(" ") + .write(serialize(coordsCur[2])).write(" ") + .write(serialize(coordsCur[3])).write(" ") + .write(serialize(coordsCur[4])).write(" ") + .write(serialize(coordsCur[5])).write(" c"); + pointPrev[0] = coordsCur[4]; + pointPrev[1] = coordsCur[5]; + break; + case PathIterator.SEG_QUADTO: + double x1 = pointPrev[0] + 2.0/3.0*(coordsCur[0] - pointPrev[0]); + double y1 = pointPrev[1] + 2.0/3.0*(coordsCur[1] - pointPrev[1]); + double x2 = coordsCur[0] + 1.0/3.0*(coordsCur[2] - coordsCur[0]); + double y2 = coordsCur[1] + 1.0/3.0*(coordsCur[3] - coordsCur[1]); + double x3 = coordsCur[2]; + double y3 = coordsCur[3]; + string.write(serialize(x1)).write(" ") + .write(serialize(y1)).write(" ") + .write(serialize(x2)).write(" ") + .write(serialize(y2)).write(" ") + .write(serialize(x3)).write(" ") + .write(serialize(y3)).write(" c"); + pointPrev[0] = x3; + pointPrev[1] = y3; + break; + case PathIterator.SEG_CLOSE: + string.write("h"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] getOutput(GraphicsState state, Resources resources, boolean first) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + if (!first) { + string.writeln("Q"); + } + string.writeln("q"); + + if (!state.getColor().equals(GraphicsState.DEFAULT_COLOR)) { + if (state.getColor().getAlpha() != GraphicsState.DEFAULT_COLOR.getAlpha()) { + double a = state.getColor().getAlpha()/255.0; + String resourceId = resources.getId(a); + string.write("/").write(resourceId).writeln(" gs"); + } + string.writeln(getOutput(state.getColor())); + } + if (!state.getTransform().equals(GraphicsState.DEFAULT_TRANSFORM)) { + string.write(getOutput(state.getTransform())).writeln(" cm"); + } + if (!state.getStroke().equals(GraphicsState.DEFAULT_STROKE)) { + string.writeln(getOutput(state.getStroke())); + } + if (state.getClip() != GraphicsState.DEFAULT_CLIP) { + string.write(getOutput(state.getClip())).writeln(" W n"); + } + if (!state.getFont().equals(GraphicsState.DEFAULT_FONT)) { + Font font = state.getFont(); + String fontResourceId = resources.getId(font); + float fontSize = font.getSize2D(); + string.write("/").write(fontResourceId).write(" ") + .write(fontSize).writeln(" Tf"); + } + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private byte[] getOutput(Stroke s) { + if (!(s instanceof BasicStroke)) { + throw new UnsupportedOperationException("Only BasicStroke objects are supported."); + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + BasicStroke strokeDefault = (BasicStroke) GraphicsState.DEFAULT_STROKE; + BasicStroke strokeNew = (BasicStroke) s; + if (strokeNew.getLineWidth() != strokeDefault.getLineWidth()) { + string.write(strokeNew.getLineWidth()).writeln(" w"); + } + if (strokeNew.getLineJoin() == BasicStroke.JOIN_MITER && strokeNew.getMiterLimit() != strokeDefault.getMiterLimit()) { + string.write(strokeNew.getMiterLimit()).writeln(" M"); + } + if (strokeNew.getLineJoin() != strokeDefault.getLineJoin()) { + string.write(STROKE_LINEJOIN.get(strokeNew.getLineJoin())).writeln(" j"); + } + if (strokeNew.getEndCap() != strokeDefault.getEndCap()) { + string.write(STROKE_ENDCAPS.get(strokeNew.getEndCap())).writeln(" J"); + } + if (strokeNew.getDashArray() != strokeDefault.getDashArray()) { + if (strokeNew.getDashArray() != null) { + string.write(serialize(strokeNew.getDashArray())).write(" ") + .write(strokeNew.getDashPhase()).writeln(" d"); + } else { + string.writeln(); + string.writeln("[] 0 d"); + } + } + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private static byte[] getOutput(AffineTransform transform) { + double[] matrix = new double[6]; + transform.getMatrix(matrix); + try { + return DataUtils.join(" ", matrix).getBytes(CHARSET); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + private static byte[] getOutput(String str, double x, double y) { + // Save current graphics state + // Undo swapping of y axis + // Render text + // Restore previous graphics state + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write("q 1 0 0 -1 ").write(x).write(" ").write(y) + .write(" cm BT ").write(getOutput(str)).write(" Tj ET Q"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private static byte[] getOutput(String str) { + // Escape string + str = str.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\t", "\\\\t") + .replaceAll("\b", "\\\\b") + .replaceAll("\f", "\\\\f") + .replaceAll("\\(", "\\\\(") + .replaceAll("\\)", "\\\\)") + .replaceAll("[\r\n]", ""); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write("(").write(str).write(")"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + private static byte[] getOutput(PDFObject image, double x, double y, + double width, double height, Resources resources) { + // Query image resource id + String resourceId = resources.getId(image); + + // Save graphics state + // Move image to correct position and scale it to (width, height) + // Swap y axis + // Draw image + // Restore old graphics state + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (FormattingWriter string = new FormattingWriter(out, CHARSET, EOL)) { + string.write("q ").write(width).write(" 0 0 ").write(height) + .write(" ").write(x).write(" ").write(y) + .write(" cm 1 0 0 -1 0 1 cm /").write(resourceId) + .write(" Do Q"); + return out.toByteArray(); + } catch (IOException e) { + return null; + } + } + + public void close() { + try { + String footer = "Q"; + if (transformed) { + footer += EOL + "Q"; + } + contents.write(footer.getBytes(CHARSET)); + contents.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFObject.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFObject.java new file mode 100644 index 00000000..0171de74 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFObject.java @@ -0,0 +1,28 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +/** + * Base interface for all structures stored in a PDF document. + */ +interface PDFObject { +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFProcessor.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFProcessor.java new file mode 100644 index 00000000..06801d56 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PDFProcessor.java @@ -0,0 +1,69 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import de.erichseifert.vectorgraphics2d.Document; +import de.erichseifert.vectorgraphics2d.Processor; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.filters.AbsoluteToRelativeTransformsFilter; +import de.erichseifert.vectorgraphics2d.intermediate.filters.FillPaintedShapeAsImageFilter; +import de.erichseifert.vectorgraphics2d.intermediate.filters.StateChangeGroupingFilter; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * {@code Processor} implementation that translates {@link CommandSequence}s to + * a {@code Document} in the Portable Document Format (PDF). + */ +public class PDFProcessor implements Processor { + private final boolean compressed; + + /** + * Initializes a {@code PDFProcessor} for compressed PDF documents. + */ + public PDFProcessor() { + this(true); + } + + /** + * Initializes a {@code PDFProcessor} with the specified compression settings. + * @param compressed {@code true} if compression is enabled, {@code false} otherwise. + */ + public PDFProcessor(boolean compressed) { + this.compressed = compressed; + } + + /** + * Returns whether the current PDF document is compressed. + * @return {@code true} if the document is compressed, {@code false} otherwise. + */ + public boolean isCompressed() { + return compressed; + } + + @Override + public Document getDocument(CommandSequence commands, PageSize pageSize) { + AbsoluteToRelativeTransformsFilter absoluteToRelativeTransformsFilter = new AbsoluteToRelativeTransformsFilter(commands); + FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(absoluteToRelativeTransformsFilter); + CommandSequence filtered = new StateChangeGroupingFilter(paintedShapeAsImageFilter); + return new PDFDocument(filtered, pageSize, isCompressed()); + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/Page.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Page.java new file mode 100644 index 00000000..7c47b370 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Page.java @@ -0,0 +1,92 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.awt.geom.Rectangle2D; + +/** + * Represents a page of a PDF document. + */ +class Page implements PDFObject { + private final Resources resources; + private final Rectangle2D mediaBox; + private final DefaultPDFObject contents; + private PageTreeNode parent; + + /** + * Initializes a {@code Page} with the specified parent node, MediaBox and contents. + * @param resources Page resources + * @param mediaBox Boundaries of the page. + * @param contents Contents of the page. + */ + public Page(Resources resources, Rectangle2D mediaBox, DefaultPDFObject contents) { + this.resources = resources; + this.mediaBox = mediaBox; + this.contents = contents; + } + + /** + * Returns the type of this object. + * The return value is always {@literal Page}. + * @return The String {@literal Page}. + */ + public String getType() { + return "Page"; + } + + public Rectangle2D getMediaBox() { + return mediaBox; + } + + /** + * Returns the immediate parent of this {@code Page}. + * @return Parent node. + */ + public PageTreeNode getParent() { + return parent; + } + + /** + * Sets the parent of this {@code Page} to the specified node. + * @param parent Immediate parent. + */ + protected void setParent(PageTreeNode parent) { + this.parent = parent; + } + + /** + * Returns the {@code Resources} object associated with this {@code Page}. + * @return Page resources. + */ + public Resources getResources() { + return resources; + } + + /** + * Returns the contents of this {@code Page}. + * @return Page contents. + */ + public DefaultPDFObject getContents() { + return contents; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/PageTreeNode.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PageTreeNode.java new file mode 100644 index 00000000..a1a6ae13 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/PageTreeNode.java @@ -0,0 +1,87 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Represents an intermediate node in the page tree of a PDF document. + */ +class PageTreeNode implements PDFObject { + private final PageTreeNode parent; + private final List children; + + /** + * Initializes a {@code PageTreeNode} with the specified parent node. + * @param parent Parent node or {@code null} to create a root node. + */ + public PageTreeNode(PageTreeNode parent) { + this.parent = parent; + this.children = new LinkedList<>(); + } + + /** + * Returns the type of this object. + * The return value is always {@literal Pages}. + * @return The String {@literal Pages}. + */ + public String getType() { + return "Pages"; + } + + /** + * Returns the parent of this node. + * If this node is a root node, the method returns {@code null}. + * @return Parent node or {@code null}, if this is a root node. + */ + public PageTreeNode getParent() { + return parent; + } + + /** + * Adds the specified {@code Page} to the node's children. + * @param page {@code Page} to be added. + */ + public void add(Page page) { + page.setParent(this); + children.add(page); + } + + /** + * Returns all {@code Page} objects that are immediate children of this node. + * @return List of child pages. + */ + public List getKids() { + return Collections.unmodifiableList(children); + } + + /** + * Returns the total number of {@code Page} objects in this subtree. + * @return Number of pages that are direct or indirect children. + */ + public int getCount() { + return children.size(); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/Payload.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Payload.java new file mode 100644 index 00000000..c97fc324 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Payload.java @@ -0,0 +1,75 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; + +import de.erichseifert.vectorgraphics2d.util.FlateEncodeStream; + +class Payload extends OutputStream { + private final ByteArrayOutputStream byteStream; + private OutputStream filteredStream; + private boolean empty; + + public Payload() { + byteStream = new ByteArrayOutputStream(); + filteredStream = byteStream; + empty = true; + } + + public byte[] getBytes() { + return byteStream.toByteArray(); + } + + @Override + public void write(int b) throws IOException { + filteredStream.write(b); + empty = false; + } + + public boolean isEmpty() { + return empty; + } + + @Override + public void close() throws IOException { + super.close(); + filteredStream.close(); + } + + public void addFilter(Class filterClass) { + if (!empty) { + throw new IllegalStateException("Cannot add filter after writing to payload."); + } + try { + filteredStream = filterClass.getConstructor(OutputStream.class) + .newInstance(filteredStream); + } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/Resources.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Resources.java new file mode 100644 index 00000000..2bd75dbe --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Resources.java @@ -0,0 +1,139 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.awt.Font; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import de.erichseifert.vectorgraphics2d.util.DataUtils; +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; + +class Resources extends DefaultPDFObject { + private static final String KEY_TRANSPARENCY = "ExtGState"; + private static final String KEY_IMAGE = "XObject"; + + private static final String[] VALUE_PROC_SET = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; + + private static final String PREFIX_FONT = "Fnt"; + private static final String PREFIX_IMAGE = "Img"; + private static final String PREFIX_TRANSPARENCY = "Trp"; + + private final List procSet; + private final Map fontsByFontId; + private final Map fontIDsByFont; + private final Map images; + private final Map transparencies; + + private final AtomicInteger currentFontId = new AtomicInteger(); + private final AtomicInteger currentImageId = new AtomicInteger(); + private final AtomicInteger currentTransparencyId = new AtomicInteger(); + + public Resources() { + super(null, null, false); + + procSet = new LinkedList<>(); + fontsByFontId = new HashMap<>(); + fontIDsByFont = new HashMap<>(); + images = new HashMap<>(); + transparencies = new HashMap<>(); + + setProcSet(VALUE_PROC_SET); + } + + private String getResourceId(Map resources, T resource, + String idPrefix, AtomicInteger idCounter) { + String id = resources.get(resource); + if (id == null) { + id = String.format("%s%d", idPrefix, idCounter.getAndIncrement()); + resources.put(resource, id); + } + return id; + } + + public String getId(Font font) { + font = GraphicsUtils.getPhysicalFont(font); + String resourceId = getResourceId(fontIDsByFont, font, PREFIX_FONT, currentFontId); + + String baseFontName = font.getPSName(); + // TODO: Determine font encoding (e.g. MacRomanEncoding, MacExpertEncoding, WinAnsiEncoding) + String fontEncoding = "WinAnsiEncoding"; + TrueTypeFont pdfFont = new TrueTypeFont(fontEncoding, baseFontName); + fontsByFontId.put(resourceId, pdfFont); + + return resourceId; + } + + public String getId(PDFObject image) { + // Make sure a dictionary entry for images exists + Map dictEntry = + (Map) dict.get(KEY_IMAGE); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_IMAGE, dictEntry); + } + + String resourceId = getResourceId(images, image, PREFIX_IMAGE, currentImageId); + dictEntry.put(resourceId, image); + + return resourceId; + } + + public String getId(Double transparency) { + // Make sure a dictionary entry for transparency levels exists + Map> dictEntry = + (Map>) dict.get(KEY_TRANSPARENCY); + if (dictEntry == null) { + dictEntry = new LinkedHashMap<>(); + dict.put(KEY_TRANSPARENCY, dictEntry); + } + + String resourceId = getResourceId(transparencies, transparency, + PREFIX_TRANSPARENCY, currentTransparencyId); + dictEntry.put(resourceId, DataUtils.map( + new String[] {"Type", "ca", "CA"}, + new Object[] {"ExtGState", transparency, transparency} + )); + + return resourceId; + } + + public void setProcSet(String... procedureNames) { + procSet.clear(); + procSet.addAll(Arrays.asList(procedureNames)); + } + + public List getProcSet() { + return Collections.unmodifiableList(procSet); + } + + public Map getFont() { + return Collections.unmodifiableMap(fontsByFontId); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/Stream.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Stream.java new file mode 100644 index 00000000..5a08afde --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/Stream.java @@ -0,0 +1,132 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.zip.DeflaterOutputStream; + +/** + * Represents a stream object in the sense of the PDF specification. + * The {@code Stream} has a defined length. + */ +class Stream extends OutputStream implements PDFObject { + public enum Filter { + FLATE + } + + private final ByteArrayOutputStream data; + private final List filters; + private OutputStream filteredData; + private boolean closed; + + /** + * Initializes a new {@code Stream}. + */ + public Stream(Filter... filters) { + data = new ByteArrayOutputStream(); + this.filters = new ArrayList<>(filters.length); + this.filters.addAll(Arrays.asList(filters)); + + filteredData = data; + for (Filter filter : filters) { + if (filter == Filter.FLATE) { + filteredData = new DeflaterOutputStream(filteredData); + } + } + } + + @Override + public void write(int b) throws IOException { + if (isClosed()) { + throw new IOException("Unable to write to closed stream."); + } + try { + this.filteredData.write(b); + } catch (IOException e) { + throw new RuntimeException("Unable to write to the output stream", e); + } + } + + /** + * Appends the specified byte array to the {@code Stream}. + * @param data Data to be appended. + */ + public void write(byte[] data) throws IOException { + if (isClosed()) { + throw new IOException("Unable to write to closed stream."); + } + try { + this.filteredData.write(data); + } catch (IOException e) { + throw new RuntimeException("Unable to write to the output stream", e); + } + } + + /** + * Returns the size of the stream contents in bytes. + * @return Number of bytes. + * @throws IllegalStateException if the stream is still open. + */ + public int getLength() { + if (!isClosed()) { + throw new IllegalStateException("Unable to determine the length of an open Stream. Close the stream first."); + } + return data.size(); + } + + /** + * Returns the content that has been written to this {@code Stream}. + * @return Stream content. + * @throws IllegalStateException if the stream is still open. + */ + public byte[] getContent() { + if (!isClosed()) { + throw new IllegalStateException("Unable to retrieve the content of an open Stream. Close the stream first."); + } + return data.toByteArray(); + } + + private boolean isClosed() { + return closed; + } + + @Override + public void close() { + closed = true; + try { + filteredData.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public List getFilters() { + return Collections.unmodifiableList(filters); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/TrueTypeFont.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/TrueTypeFont.java new file mode 100644 index 00000000..3dd557f4 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/TrueTypeFont.java @@ -0,0 +1,75 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.pdf; + +/** + * Represents a TrueType font in the sense of the PDF specification. + */ +class TrueTypeFont implements PDFObject { + private final String encoding; + private final String baseFont; + + /** + * Creates a {@code TrueTypeFont} with the specified encoding and base font. + * @param encoding Used encoding. + * @param baseFont Base font name. + */ + public TrueTypeFont(String encoding, String baseFont) { + this.encoding = encoding; + this.baseFont = baseFont; + } + + /** + * Returns the encoding of this font. + * @return Encoding. + */ + public String getEncoding() { + return encoding; + } + + /** + * Returns the type of this object. + * Always returns "Font". + * @return The String "Font". + */ + public String getType() { + return "Font"; + } + + /** + * Returns the subtype of this object. + * Always returns "TrueType". + * @return The String "TrueType". + */ + public String getSubtype() { + return "TrueType"; + } + + /** + * Returns the name of the base font. + * @return Base font name. + */ + public String getBaseFont() { + return baseFont; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/pdf/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/pdf/package-info.java new file mode 100644 index 00000000..94d0d18c --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/pdf/package-info.java @@ -0,0 +1,26 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Implementation of a processor to convert VectorGraphics2D intermediate + * language commands to the Portable Document Format (PDF). + */ +package de.erichseifert.vectorgraphics2d.pdf; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGDocument.java b/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGDocument.java new file mode 100644 index 00000000..63530c0c --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGDocument.java @@ -0,0 +1,599 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.svg; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; +import javax.imageio.ImageIO; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import de.erichseifert.vectorgraphics2d.GraphicsState; +import de.erichseifert.vectorgraphics2d.SizedDocument; +import de.erichseifert.vectorgraphics2d.VectorHints; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.commands.AffineTransformCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Command; +import de.erichseifert.vectorgraphics2d.intermediate.commands.CreateCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DisposeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawImageCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.DrawStringCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.FillShapeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.Group; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetBackgroundCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetClipCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetColorCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetCompositeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetFontCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetHintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetPaintCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetStrokeCommand; +import de.erichseifert.vectorgraphics2d.intermediate.commands.SetTransformCommand; +import de.erichseifert.vectorgraphics2d.util.Base64EncodeStream; +import de.erichseifert.vectorgraphics2d.util.DataUtils; +import de.erichseifert.vectorgraphics2d.util.GraphicsUtils; +import de.erichseifert.vectorgraphics2d.util.PageSize; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; + +/** + * Represents a {@code Document} in the Scaled Vector Graphics (SVG) + * format. + */ +// TODO Implement composite support for SVG (filters?) +// TODO Implement paint support for SVG +class SVGDocument extends SizedDocument { + private static final String SVG_DOCTYPE_QNAME = "svg"; + private static final String SVG_DOCTYPE_PUBLIC_ID = "-//W3C//DTD SVG 1.1//EN"; + private static final String SVG_DOCTYPE_SYSTEM_ID = "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; + private static final String SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg"; + private static final String XLINK_NAMESPACE = "xlink"; + private static final String XLINK_NAMESPACE_URI = "http://www.w3.org/1999/xlink"; + + private static final String PREFIX_CLIP = "clip"; + + // TODO Resolution settings + private static final String CHARSET = "UTF-8"; + + private final Stack states; + + private final Document doc; + private final Element root; + private Element group; + private boolean groupAdded; + + private Element defs; + private final Map clippingPathElements; + + /** Mapping of stroke endcap values from Java to SVG. */ + private static final Map STROKE_ENDCAPS = DataUtils.map( + new Integer[] { BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE }, + new String[] { "butt", "round", "square" } + ); + + /** Mapping of line join values for path drawing from Java to SVG. */ + private static final Map STROKE_LINEJOIN = DataUtils.map( + new Integer[] { BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL }, + new String[] { "miter", "round", "bevel" } + ); + + public SVGDocument(CommandSequence commands, PageSize pageSize) { + super(pageSize, true); + + states = new Stack<>(); + states.push(new GraphicsState()); + clippingPathElements = new HashMap<>(); + + // Prepare DOM + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + docFactory.setValidating(false); + DocumentBuilder docBuilder; + try { + docBuilder = docFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException("Could not create XML builder."); + } + + // Create XML document with DOCTYPE + DOMImplementation domImpl = docBuilder.getDOMImplementation(); + DocumentType docType = domImpl.createDocumentType(SVG_DOCTYPE_QNAME, SVG_DOCTYPE_PUBLIC_ID, SVG_DOCTYPE_SYSTEM_ID); + doc = domImpl.createDocument(SVG_NAMESPACE_URI, "svg", docType); + doc.setXmlStandalone(false); + + root = doc.getDocumentElement(); + initRoot(); + + group = root; + for (Command command : commands) { + handle(command); + } + } + + private GraphicsState getCurrentState() { + return states.peek(); + } + + private void initRoot() { + double x = getPageSize().getX(); + double y = getPageSize().getY(); + double width = getPageSize().getWidth(); + double height = getPageSize().getHeight(); + + // Add svg element + root.setAttribute("xmlns:" + XLINK_NAMESPACE, XLINK_NAMESPACE_URI); + root.setAttribute("version", "1.1"); + root.setAttribute("x", DataUtils.format(x) + "px"); + root.setAttribute("y", DataUtils.format(y) + "px"); + root.setAttribute("width", DataUtils.format(width) + "px"); + root.setAttribute("height", DataUtils.format(height) + "px"); + root.setAttribute("viewBox", DataUtils.join(" ", new double[] {x, y, width, height})); + } + + public void writeTo(OutputStream out) throws IOException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.STANDALONE, "no"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.setOutputProperty(OutputKeys.ENCODING, CHARSET); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, + doc.getDoctype().getPublicId()); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, + doc.getDoctype().getSystemId()); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + } catch (TransformerException e) { + throw new IOException(e.getMessage()); + } + } + + @Override + public String toString() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + writeTo(out); + return out.toString(CHARSET); + } catch (IOException e) { + return ""; + } + } + + private void newGroup() { + group = doc.createElement("g"); + groupAdded = false; + + Shape clip = getCurrentState().getClip(); + if (clip != GraphicsState.DEFAULT_CLIP) { + Element clipElem = getClipElement(clip); + String ref = "url(#" + clipElem.getAttribute("id") + ")"; + group.setAttribute("clip-path", ref); + } + + AffineTransform tx = getCurrentState().getTransform(); + if (!GraphicsState.DEFAULT_TRANSFORM.equals(tx)) { + group.setAttribute("transform", getOutput(tx)); + } + } + + private Element getClipElement(Shape clip) { + // Look for existing entries + Element path = clippingPathElements.get(clip.hashCode()); + if (path != null) { + return path; + } + + // Make sure exists + if (defs == null) { + defs = doc.createElement("defs"); + root.insertBefore(defs, root.getFirstChild()); + } + + // Store clipping path in without styling information + path = doc.createElement("clipPath"); + path.setAttribute("id", PREFIX_CLIP + clip.hashCode()); + Element shape = getElement(clip); + shape.removeAttribute("style"); + path.appendChild(shape); + defs.appendChild(path); + + // Register path + clippingPathElements.put(clip.hashCode(), path); + + return path; + } + + private void addToGroup(Element e) { + group.appendChild(e); + if (!groupAdded && group != root) { + root.appendChild(group); + groupAdded = true; + } + } + + public void handle(Command command) { + if (command instanceof Group) { + Group c = (Group) command; + applyStateCommands(c.getValue()); + if (containsGroupCommand(c.getValue())) { + newGroup(); + } + } else if (command instanceof DrawImageCommand) { + DrawImageCommand c = (DrawImageCommand) command; + Element e = getElement(c.getValue(), + c.getX(), c.getY(), c.getWidth(), c.getHeight()); + addToGroup(e); + } else if (command instanceof DrawShapeCommand) { + DrawShapeCommand c = (DrawShapeCommand) command; + Element e = getElement(c.getValue()); + e.setAttribute("style", getStyle(false)); + addToGroup(e); + } else if (command instanceof DrawStringCommand) { + DrawStringCommand c = (DrawStringCommand) command; + Element e = getElement(c.getValue(), c.getX(), c.getY()); + e.setAttribute("style", getStyle(getCurrentState().getFont())); + addToGroup(e); + } else if (command instanceof FillShapeCommand) { + FillShapeCommand c = (FillShapeCommand) command; + Shape shape = c.getValue(); + Element e = getElement(shape); + if (shape instanceof Path2D) { + Path2D path = (Path2D) shape; + e.setAttribute("style", getStyle(true, path.getWindingRule() == Path2D.WIND_NON_ZERO)); + } else { + e.setAttribute("style", getStyle(true)); + } + addToGroup(e); + } + } + + private void applyStateCommands(List> commands) { + for (Command command : commands) { + GraphicsState state = getCurrentState(); + if (command instanceof SetBackgroundCommand) { + SetBackgroundCommand c = (SetBackgroundCommand) command; + state.setBackground(c.getValue()); + } else if (command instanceof SetClipCommand) { + SetClipCommand c = (SetClipCommand) command; + state.setClip(c.getValue()); + } else if (command instanceof SetColorCommand) { + SetColorCommand c = (SetColorCommand) command; + state.setColor(c.getValue()); + } else if (command instanceof SetCompositeCommand) { + SetCompositeCommand c = (SetCompositeCommand) command; + state.setComposite(c.getValue()); + } else if (command instanceof SetFontCommand) { + SetFontCommand c = (SetFontCommand) command; + state.setFont(c.getValue()); + } else if (command instanceof SetPaintCommand) { + SetPaintCommand c = (SetPaintCommand) command; + state.setPaint(c.getValue()); + } else if (command instanceof SetStrokeCommand) { + SetStrokeCommand c = (SetStrokeCommand) command; + state.setStroke(c.getValue()); + } else if (command instanceof SetTransformCommand) { + SetTransformCommand c = (SetTransformCommand) command; + state.setTransform(c.getValue()); + } else if (command instanceof AffineTransformCommand) { + AffineTransformCommand c = (AffineTransformCommand) command; + AffineTransform stateTransform = state.getTransform(); + AffineTransform transformToBeApplied = c.getValue(); + stateTransform.concatenate(transformToBeApplied); + state.setTransform(stateTransform); + } else if (command instanceof SetHintCommand) { + SetHintCommand c = (SetHintCommand) command; + state.getHints().put(c.getKey(), c.getValue()); + } else if (command instanceof CreateCommand) { + try { + states.push((GraphicsState) getCurrentState().clone()); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else if (command instanceof DisposeCommand) { + states.pop(); + } + } + } + + private boolean containsGroupCommand(List> commands) { + for (Command command : commands) { + if ((command instanceof SetClipCommand) || + (command instanceof SetTransformCommand) || + (command instanceof AffineTransformCommand)) { + return true; + } + } + return false; + } + + private String getStyle(boolean filled) { + return getStyle(filled, true); + } + + private String getStyle(boolean filled, boolean fillRullNonZero) { + StringBuilder style = new StringBuilder(); + + Color color = getCurrentState().getColor(); + String colorOutput = getOutput(color); + double opacity = color.getAlpha()/255.0; + + if (filled) { + appendStyle(style, "fill", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "fill-opacity", opacity); + } + if (!fillRullNonZero) { + // nonzero is the default; only need to set the style rule for non-default evenodd winding rule. + appendStyle(style, "fill-rule", "evenodd"); + } + } else { + appendStyle(style, "fill", "none"); + } + + if (!filled) { + appendStyle(style, "stroke", colorOutput); + if (color.getAlpha() < 255) { + appendStyle(style, "stroke-opacity", opacity); + } + Stroke stroke = getCurrentState().getStroke(); + if (stroke instanceof BasicStroke) { + BasicStroke bs = (BasicStroke) stroke; + if (bs.getLineWidth() != 1f) { + appendStyle(style, "stroke-width", bs.getLineWidth()); + } + if (bs.getMiterLimit() != 4f) { + appendStyle(style, "stroke-miterlimit", bs.getMiterLimit()); + } + if (bs.getEndCap() != BasicStroke.CAP_BUTT) { + appendStyle(style, "stroke-linecap", STROKE_ENDCAPS.get(bs.getEndCap())); + } + if (bs.getLineJoin() != BasicStroke.JOIN_MITER) { + appendStyle(style, "stroke-linejoin", STROKE_LINEJOIN.get(bs.getLineJoin())); + } + if (bs.getDashArray() != null) { + appendStyle(style, "stroke-dasharray", DataUtils.join(",", bs.getDashArray())); + if (bs.getDashPhase() != 0f) { + appendStyle(style, "stroke-dashoffset", bs.getDashPhase()); + } + } + } + } else { + appendStyle(style, "stroke", "none"); + } + + return style.toString(); + } + + private String getStyle(Font font) { + String style = getStyle(true); + if (!GraphicsState.DEFAULT_FONT.equals(font)) { + style += getOutput(font); + } + return style; + } + + private static void appendStyle(StringBuilder style, String attribute, Object value) { + style.append(attribute).append(":") + .append(DataUtils.format(value)).append(";"); + } + + private static String getOutput(AffineTransform tx) { + StringBuilder out = new StringBuilder(); + // FIXME: Use tx.getType() to check for transformation components + if (AffineTransform.getTranslateInstance(tx.getTranslateX(), + tx.getTranslateY()).equals(tx)) { + out.append("translate(") + .append(DataUtils.format(tx.getTranslateX())).append(" ") + .append(DataUtils.format(tx.getTranslateY())).append(")"); + } else { + double[] matrix = new double[6]; + tx.getMatrix(matrix); + out.append("matrix(").append(DataUtils.join(" ", matrix)).append(")"); + } + return out.toString(); + } + + private static String getOutput(Color color) { + if (color.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + float[] cmyk = color.getComponents(null); + return String.format((Locale) null, + "rgb(%d,%d,%d) icc-color(Generic-CMYK-profile,%f,%f,%f,%f)", + color.getRed(), color.getGreen(), color.getBlue(), + cmyk[0], cmyk[1], cmyk[2], cmyk[3]); + } else { + return String.format((Locale) null, "rgb(%d,%d,%d)", + color.getRed(), color.getGreen(), color.getBlue()); + } + } + + private static String getOutput(Shape shape) { + StringBuilder out = new StringBuilder(); + PathIterator segments = shape.getPathIterator(null); + double[] coords = new double[6]; + for (int i = 0; !segments.isDone(); i++, segments.next()) { + if (i > 0) { + out.append(" "); + } + int segmentType = segments.currentSegment(coords); + switch (segmentType) { + case PathIterator.SEG_MOVETO: + out.append("M").append(DataUtils.format(coords[0])).append(",").append(DataUtils.format(coords[1])); + break; + case PathIterator.SEG_LINETO: + out.append("L").append(coords[0]).append(",").append(DataUtils.format(coords[1])); + break; + case PathIterator.SEG_CUBICTO: + out.append("C") + .append(DataUtils.format(coords[0])).append(",").append(DataUtils.format(coords[1])).append(" ") + .append(DataUtils.format(coords[2])).append(",").append(DataUtils.format(coords[3])).append(" ") + .append(DataUtils.format(coords[4])).append(",").append(DataUtils.format(coords[5])); + break; + case PathIterator.SEG_QUADTO: + out.append("Q") + .append(DataUtils.format(coords[0])).append(",").append(DataUtils.format(coords[1])).append(" ") + .append(DataUtils.format(coords[2])).append(",").append(DataUtils.format(coords[3])); + break; + case PathIterator.SEG_CLOSE: + out.append("Z"); + break; + default: + throw new IllegalStateException("Unknown path operation."); + } + } + return out.toString(); + } + + private static String getOutput(Font font) { + StringBuilder out = new StringBuilder(); + if (!GraphicsState.DEFAULT_FONT.getFamily().equals(font.getFamily())) { + String physicalFamily = GraphicsUtils.getPhysicalFont(font).getFamily(); + out.append("font-family:\"").append(physicalFamily).append("\";"); + } + if (font.getSize2D() != GraphicsState.DEFAULT_FONT.getSize2D()) { + out.append("font-size:").append(DataUtils.format(font.getSize2D())).append("px;"); + } + if ((font.getStyle() & Font.ITALIC) != 0) { + out.append("font-style:italic;"); + } + if ((font.getStyle() & Font.BOLD) != 0) { + out.append("font-weight:bold;"); + } + return out.toString(); + } + + private static String getOutput(Image image, boolean lossyAllowed) { + BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image); + + String encoded = encodeImage(bufferedImage, "png"); + if (!GraphicsUtils.usesAlpha(bufferedImage) && lossyAllowed) { + String encodedLossy = encodeImage(bufferedImage, "jpeg"); + if (encodedLossy.length() > 0 && encodedLossy.length() < encoded.length()) { + encoded = encodedLossy; + } + } + + return encoded; + } + + private static String encodeImage(BufferedImage bufferedImage, String format) { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + Base64EncodeStream encodeStream = new Base64EncodeStream(byteStream); + try { + ImageIO.write(bufferedImage, format, encodeStream); + encodeStream.close(); + String encoded = byteStream.toString("ISO-8859-1"); + return String.format("data:image/%s;base64,%s", format, encoded); + } catch (IOException e) { + return ""; + } + } + + private Element getElement(Shape shape) { + Element elem; + if (shape instanceof Line2D) { + Line2D s = (Line2D) shape; + elem = doc.createElement("line"); + elem.setAttribute("x1", DataUtils.format(s.getX1())); + elem.setAttribute("y1", DataUtils.format(s.getY1())); + elem.setAttribute("x2", DataUtils.format(s.getX2())); + elem.setAttribute("y2", DataUtils.format(s.getY2())); + } else if (shape instanceof Rectangle2D) { + Rectangle2D s = (Rectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + } else if (shape instanceof RoundRectangle2D) { + RoundRectangle2D s = (RoundRectangle2D) shape; + elem = doc.createElement("rect"); + elem.setAttribute("x", DataUtils.format(s.getX())); + elem.setAttribute("y", DataUtils.format(s.getY())); + elem.setAttribute("width", DataUtils.format(s.getWidth())); + elem.setAttribute("height", DataUtils.format(s.getHeight())); + elem.setAttribute("rx", DataUtils.format(s.getArcWidth()/2.0)); + elem.setAttribute("ry", DataUtils.format(s.getArcHeight()/2.0)); + } else if (shape instanceof Ellipse2D) { + Ellipse2D s = (Ellipse2D) shape; + elem = doc.createElement("ellipse"); + elem.setAttribute("cx", DataUtils.format(s.getCenterX())); + elem.setAttribute("cy", DataUtils.format(s.getCenterY())); + elem.setAttribute("rx", DataUtils.format(s.getWidth()/2.0)); + elem.setAttribute("ry", DataUtils.format(s.getHeight()/2.0)); + } else { + elem = doc.createElement("path"); + elem.setAttribute("d", getOutput(shape)); + } + return elem; + } + + private Element getElement(String text, double x, double y) { + Element elem = doc.createElement("text"); + elem.appendChild(doc.createTextNode(text)); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + return elem; + } + + private Element getElement(Image image, double x, double y, double width, double height) { + Element elem = doc.createElement("image"); + elem.setAttribute("x", DataUtils.format(x)); + elem.setAttribute("y", DataUtils.format(y)); + elem.setAttribute("width", DataUtils.format(width)); + elem.setAttribute("height", DataUtils.format(height)); + elem.setAttribute("preserveAspectRatio", "none"); + boolean lossyAllowed = getCurrentState().getHints().get(VectorHints.KEY_EXPORT) == + VectorHints.VALUE_EXPORT_SIZE; + elem.setAttribute("xlink:href", getOutput(image, lossyAllowed)); + return elem; + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGProcessor.java b/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGProcessor.java new file mode 100644 index 00000000..3ed25def --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/svg/SVGProcessor.java @@ -0,0 +1,47 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.svg; + +import de.erichseifert.vectorgraphics2d.Document; +import de.erichseifert.vectorgraphics2d.Processor; +import de.erichseifert.vectorgraphics2d.intermediate.CommandSequence; +import de.erichseifert.vectorgraphics2d.intermediate.filters.FillPaintedShapeAsImageFilter; +import de.erichseifert.vectorgraphics2d.intermediate.filters.StateChangeGroupingFilter; +import de.erichseifert.vectorgraphics2d.util.PageSize; + +/** + * {@code Processor} implementation that translates {@link CommandSequence}s to + * a {@code Document} in the Scaled Vector Graphics (SVG) format. + */ +public class SVGProcessor implements Processor { + /** + * Initializes an {@code SVGProcessor}. + */ + public SVGProcessor() {} + + @Override + public Document getDocument(CommandSequence commands, PageSize pageSize) { + FillPaintedShapeAsImageFilter shapesAsImages = new FillPaintedShapeAsImageFilter(commands); + CommandSequence filtered = new StateChangeGroupingFilter(shapesAsImages); + return new SVGDocument(filtered, pageSize); + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/svg/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/svg/package-info.java new file mode 100644 index 00000000..5a34d48a --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/svg/package-info.java @@ -0,0 +1,26 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Implementation of a processor to convert VectorGraphics2D intermediate + * language commands to Scaled Vector Graphics (SVG). + */ +package de.erichseifert.vectorgraphics2d.svg; diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/ASCII85EncodeStream.java b/Java/src/de/erichseifert/vectorgraphics2d/util/ASCII85EncodeStream.java new file mode 100644 index 00000000..97029895 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/ASCII85EncodeStream.java @@ -0,0 +1,125 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class ASCII85EncodeStream extends FilterOutputStream { + private static final int BASE = 85; + private static final int[] POW_85 = + { BASE*BASE*BASE*BASE, BASE*BASE*BASE, BASE*BASE, BASE, 1 }; + private static final char[] CHAR_MAP = + "!\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu" + .toCharArray(); + + private boolean closed; + + private final byte[] data; + private int dataSize; + + private final byte[] prefixBytes; + private boolean prefixDone; + private final byte[] suffixBytes; + + private final byte[] encoded; + + public ASCII85EncodeStream(OutputStream out, String prefix, String suffix) { + super(out); + prefixBytes = (prefix != null) ? prefix.getBytes() : "".getBytes(); + suffixBytes = (suffix != null) ? suffix.getBytes() : "".getBytes(); + data = new byte[4]; + encoded = new byte[5]; + } + + public ASCII85EncodeStream(OutputStream out) { + this(out, "", "~>"); + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (!prefixDone) { + out.write(prefixBytes); + prefixDone = true; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + for (int i = 0; i < 4 && i < size; i++) { + uint32 |= (bytes[i] & 0xff) << (3 - i)*8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) 0); + if (uint32 == 0L && padByteCount == 0) { + encoded[0] = 'z'; + return 1; + } + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32/POW_85[i]%BASE)]; + } + return size; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + out.write(suffixBytes); + + super.close(); + closed = true; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/AlphaToMaskOp.java b/Java/src/de/erichseifert/vectorgraphics2d/util/AlphaToMaskOp.java new file mode 100644 index 00000000..c153dac4 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/AlphaToMaskOp.java @@ -0,0 +1,122 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.Hashtable; + +public class AlphaToMaskOp implements BufferedImageOp { + private final boolean inverted; + + public AlphaToMaskOp(boolean inverted) { + this.inverted = inverted; + } + + public AlphaToMaskOp() { + this(false); + } + + public boolean isInverted() { + return inverted; + } + + public BufferedImage filter(BufferedImage src, BufferedImage dest) { + ColorModel cm = src.getColorModel(); + + if (dest == null) { + dest = createCompatibleDestImage(src, cm); + } else if (dest.getWidth() != src.getWidth() || dest.getHeight() != src.getHeight()) { + throw new IllegalArgumentException("Source and destination images have different dimensions."); + } else if (dest.getColorModel() != cm) { + throw new IllegalArgumentException("Color models don't match."); + } + + if (cm.hasAlpha()) { + Raster srcRaster = src.getRaster(); + WritableRaster destRaster = dest.getRaster(); + + for (int y = 0; y < srcRaster.getHeight(); y++) { + for (int x = 0; x < srcRaster.getWidth(); x++) { + int argb = cm.getRGB(srcRaster.getDataElements(x, y, null)); + int alpha = argb >>> 24; + if (alpha >= 127 && !isInverted() || alpha < 127 && isInverted()) { + argb |= 0xff000000; + } else { + argb &= 0x00ffffff; + } + destRaster.setDataElements(x, y, cm.getDataElements(argb, null)); + } + } + } + + return dest; + } + + public Rectangle2D getBounds2D(BufferedImage src) { + Rectangle2D bounds = new Rectangle2D.Double(); + bounds.setRect(src.getRaster().getBounds()); + return bounds; + } + + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM) { + if (destCM == null) { + destCM = src.getColorModel(); + } + WritableRaster raster = destCM.createCompatibleWritableRaster( + src.getWidth(), src.getHeight()); + boolean isRasterPremultiplied = destCM.isAlphaPremultiplied(); + Hashtable properties = null; + if (src.getPropertyNames() != null) { + properties = new Hashtable<>(); + for (String key : src.getPropertyNames()) { + properties.put(key, src.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(destCM, raster, + isRasterPremultiplied, properties); + src.copyData(raster); + return bimage; + } + + public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { + if (dstPt == null) { + dstPt = new Point2D.Double(); + } + dstPt.setLocation(srcPt); + return dstPt; + } + + public RenderingHints getRenderingHints() { + return null; + } + +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/Base64EncodeStream.java b/Java/src/de/erichseifert/vectorgraphics2d/util/Base64EncodeStream.java new file mode 100644 index 00000000..bc9b7abf --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/Base64EncodeStream.java @@ -0,0 +1,107 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class Base64EncodeStream extends FilterOutputStream { + private static final int BASE = 64; + private static final int[] POW_64 = + { BASE*BASE*BASE, BASE*BASE, BASE, 1 }; + private static final char[] CHAR_MAP = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + .toCharArray(); + + private boolean closed; + + private final byte[] data; + private int dataSize; + + private final byte[] encoded; + + public Base64EncodeStream(OutputStream out) { + super(out); + data = new byte[3]; + encoded = new byte[4]; + } + + @Override + public void write(int b) throws IOException { + if (closed) { + return; + } + if (dataSize == data.length) { + writeChunk(); + dataSize = 0; + } + data[dataSize++] = (byte) (b & 0xff); + } + + private void writeChunk() throws IOException { + if (dataSize == 0) { + return; + } + long uint32 = toUInt32(data, dataSize); + int padByteCount = data.length - dataSize; + int encodedSize = encodeChunk(uint32, padByteCount); + out.write(encoded, 0, encodedSize); + } + + private static long toUInt32(byte[] bytes, int size) { + long uint32 = 0L; + int offset = (3 - size)*8; + for (int i = size - 1; i >= 0; i--) { + uint32 |= (bytes[i] & 0xff) << offset; + offset += 8; + } + return toUnsignedInt(uint32); + } + + private static long toUnsignedInt(long x) { + return x & 0x00000000ffffffffL; + } + + private int encodeChunk(long uint32, int padByteCount) { + Arrays.fill(encoded, (byte) '='); + int size = encoded.length - padByteCount; + for (int i = 0; i < size; i++) { + encoded[i] = (byte) CHAR_MAP[(int) (uint32/POW_64[i]%BASE)]; + } + return encoded.length; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + + writeChunk(); + + super.close(); + closed = true; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/DataUtils.java b/Java/src/de/erichseifert/vectorgraphics2d/util/DataUtils.java new file mode 100644 index 00000000..fa3c9178 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/DataUtils.java @@ -0,0 +1,266 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Abstract class that contains utility functions for working with data + * collections like maps or lists. + */ +public abstract class DataUtils { + /** Standard pattern to format numbers */ + private static final DecimalFormat DOUBLE_FORMAT = + new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + private static final DecimalFormat FLOAT_FORMAT = + new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + + static { + DOUBLE_FORMAT.setMaximumFractionDigits(15); + FLOAT_FORMAT.setMaximumFractionDigits(6); + } + + /** + * Default constructor that prevents creation of class. + */ + DataUtils() { + throw new UnsupportedOperationException(); + } + + /** + * Creates a mapping from two arrays, one with keys, one with values. + * @param Data type of the keys. + * @param Data type of the values. + * @param keys Array containing the keys. + * @param values Array containing the values. + * @return Map with keys and values from the specified arrays. + */ + public static Map map(K[] keys, V[] values) { + // Check for valid parameters + if (keys.length != values.length) { + throw new IllegalArgumentException( + "Cannot create a Map: " + + "The number of keys and values differs."); + } + // Fill map with keys and values + Map map = new LinkedHashMap<>(keys.length); + for (int i = 0; i < keys.length; i++) { + K key = keys[i]; + V value = values[i]; + map.put(key, value); + } + return map; + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * @param separator Separator string. + * @param elements List of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, List elements) { + if (elements == null || elements.size() == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(elements.size()*3); + int i = 0; + for (Object elem : elements) { + if (separator.length() > 0 && i++ > 0) { + sb.append(separator); + } + sb.append(format(elem)); + } + return sb.toString(); + } + + /** + * Returns a string containing all elements concatenated by a specified + * separator. + * @param separator Separator string. + * @param elements Array of elements that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, Object[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + return join(separator, Arrays.asList(elements)); + } + + /** + * Returns a string containing all double numbers concatenated by a + * specified separator. + * @param separator Separator string. + * @param elements Array of double numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, double[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList<>(elements.length); + for (Double element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns a string containing all float numbers concatenated by a + * specified separator. + * @param separator Separator string. + * @param elements Array of float numbers that should be concatenated. + * @return a concatenated string. + */ + public static String join(String separator, float[] elements) { + if (elements == null || elements.length == 0) { + return ""; + } + List list = new ArrayList<>(elements.length); + for (Float element : elements) { + list.add(element); + } + return join(separator, list); + } + + /** + * Returns the largest of all specified values. + * @param values Several integer values. + * @return largest value. + */ + public static int max(int... values) { + if (values.length == 0) { + throw new IllegalArgumentException("No values provided: Cannot determine maximum value."); + } + int max = values[0]; + for (int i = 1; i < values.length; i++) { + if (values[i] > max) { + max = values[i]; + } + } + return max; + } + + /** + * Copies data from an input stream to an output stream using a buffer of + * specified size. + * @param in Input stream. + * @param out Output stream. + * @param bufferSize Size of the copy buffer. + * @throws IOException when an error occurs while copying. + */ + public static void transfer(InputStream in, OutputStream out, int bufferSize) + throws IOException { + byte[] buffer = new byte[bufferSize]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + + /** + * Returns a formatted string of the specified number. All trailing zeroes + * or decimal points will be stripped. + * @param number Number to convert to a string. + * @return A formatted string. + */ + public static String format(Number number) { + String formatted; + if (number instanceof Double) { + formatted = DOUBLE_FORMAT.format(number.doubleValue()); + } else if (number instanceof Float) { + formatted = FLOAT_FORMAT.format(number.floatValue()); + } else { + formatted = number.toString(); + } + return formatted; + } + + /** + * Returns a formatted string of the specified object. + * @param obj Object to convert to a string. + * @return A formatted string. + */ + public static String format(Object obj) { + if (obj instanceof Number) { + return format((Number) obj); + } else { + return obj.toString(); + } + } + + /** + * Converts an array of {@code float} numbers to a list of {@code Float}s. + * The list will be empty if the array is empty or {@code null}. + * @param elements Array of float numbers. + * @return A list with all numbers as {@code Float}. + */ + public static List asList(float[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList<>(size); + if (elements != null) { + for (Float elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Converts an array of {@code double} numbers to a list of {@code Double}s. + * The list will be empty if the array is empty or {@code null}. + * @param elements Array of double numbers. + * @return A list with all numbers as {@code Double}. + */ + public static List asList(double[] elements) { + int size = (elements != null) ? elements.length : 0; + List list = new ArrayList<>(size); + if (elements != null) { + for (Double elem : elements) { + list.add(elem); + } + } + return list; + } + + /** + * Removes the specified trailing pattern from a string. + * @param s string. + * @param substr trailing pattern. + * @return A string without the trailing pattern. + */ + public static String stripTrailing(String s, String substr) { + return s.replaceAll("(" + Pattern.quote(substr) + ")+$", ""); + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/FlateEncodeStream.java b/Java/src/de/erichseifert/vectorgraphics2d/util/FlateEncodeStream.java new file mode 100644 index 00000000..2f592911 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/FlateEncodeStream.java @@ -0,0 +1,32 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; + +public class FlateEncodeStream extends DeflaterOutputStream { + public FlateEncodeStream(OutputStream out) { + super(out); + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/FormattingWriter.java b/Java/src/de/erichseifert/vectorgraphics2d/util/FormattingWriter.java new file mode 100644 index 00000000..19c4641d --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/FormattingWriter.java @@ -0,0 +1,102 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +public class FormattingWriter implements Closeable, Flushable, AutoCloseable { + private final OutputStream out; + private final String encoding; + private final byte[] eolBytes; + private long position; + + public FormattingWriter(OutputStream out, String encoding, String eol) throws UnsupportedEncodingException { + if (out == null) { + throw new IllegalArgumentException("Output stream cannot be null."); + } + if (eol == null || eol.isEmpty()) { + throw new IllegalArgumentException("End-of-line string cannot be empty."); + } + this.out = out; + this.encoding = encoding; + this.eolBytes = eol.getBytes(encoding); + } + + public FormattingWriter write(byte[] bytes) throws IOException { + out.write(bytes, 0, bytes.length); + position += bytes.length; + return this; + } + + public FormattingWriter write(String str) throws IOException { + byte[] bytes = str.getBytes(encoding); + return write(bytes); + } + + public FormattingWriter write(String format, Object... args) throws IOException { + return write(String.format(null, format, args)); + } + + public FormattingWriter write(Number number) throws IOException { + return write(DataUtils.format(number)); + } + + public FormattingWriter writeln() throws IOException { + return write(eolBytes); + } + + public FormattingWriter writeln(byte[] bytes) throws IOException { + write(bytes); + return writeln(); + } + + public FormattingWriter writeln(String string) throws IOException { + write(string); + return writeln(); + } + + public FormattingWriter writeln(String format, Object... args) throws IOException { + write(String.format(null, format, args)); + return writeln(); + } + + public FormattingWriter writeln(Number number) throws IOException { + write(number); + return writeln(); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + + public long tell() { + return position; + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/GraphicsUtils.java b/Java/src/de/erichseifert/vectorgraphics2d/util/GraphicsUtils.java new file mode 100644 index 00000000..67f4ed0b --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/GraphicsUtils.java @@ -0,0 +1,431 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.Arc2D; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.QuadCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.PixelGrabber; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.WritableRaster; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +import javax.swing.ImageIcon; + +/** + * Abstract class that contains utility functions for working with graphics. + * For example, this includes font handling. + */ +public abstract class GraphicsUtils { + private static final FontRenderContext FONT_RENDER_CONTEXT = + new FontRenderContext(null, false, true); + private static final String FONT_TEST_STRING = + "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"; + + /** + * Default constructor that prevents creation of class. + */ + GraphicsUtils() { + throw new UnsupportedOperationException(); + } + + /** + * This method returns {@code true} if the specified image has the + * possibility to store transparent pixels. + * Inspired by http://www.exampledepot.com/egs/java.awt.image/HasAlpha.html + * @param image Image that should be checked for alpha channel. + * @return {@code true} if the specified image can have transparent pixels, + * {@code false} otherwise + */ + public static boolean hasAlpha(Image image) { + ColorModel cm; + // If buffered image, the color model is readily available + if (image instanceof BufferedImage) { + BufferedImage bimage = (BufferedImage) image; + cm = bimage.getColorModel(); + } else { + // Use a pixel grabber to retrieve the image's color model; + // grabbing a single pixel is usually sufficient + PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + return false; + } + // Get the image's color model + cm = pg.getColorModel(); + } + return cm.hasAlpha(); + } + + /** + * This method returns {@code true} if the specified image has at least one + * pixel that is not fully opaque. + * @param image Image that should be checked for non-opaque pixels. + * @return {@code true} if the specified image has transparent pixels, + * {@code false} otherwise + */ + public static boolean usesAlpha(Image image) { + if (image == null) { + return false; + } + BufferedImage bimage = toBufferedImage(image); + Raster alphaRaster = bimage.getAlphaRaster(); + if (alphaRaster == null) { + return false; + } + DataBuffer dataBuffer = alphaRaster.getDataBuffer(); + final int elemBits = DataBuffer.getDataTypeSize(dataBuffer.getDataType()); + final int alphaBits = elemBits/bimage.getRaster().getNumBands(); + final int alphaShift = (elemBits - alphaBits); + for (int i = 0; i < dataBuffer.getSize(); i++) { + int alpha = dataBuffer.getElem(i) >>> alphaShift; + if (alpha < 255) { + return true; + } + } + return false; + } + + /** + * Converts a rendered image instance to a {@code BufferedImage}. + * @param image Rendered image that should be converted. + * @return a buffered image containing the image pixels, or the original + * instance if the image already was of type {@code BufferedImage}. + */ + public static BufferedImage toBufferedImage(RenderedImage image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + + ColorModel cm = image.getColorModel(); + WritableRaster raster = cm.createCompatibleWritableRaster( + image.getWidth(), image.getHeight()); + boolean isRasterPremultiplied = cm.isAlphaPremultiplied(); + Hashtable properties = null; + if (image.getPropertyNames() != null) { + properties = new Hashtable<>(); + for (String key : image.getPropertyNames()) { + properties.put(key, image.getProperty(key)); + } + } + + BufferedImage bimage = new BufferedImage(cm, raster, + isRasterPremultiplied, properties); + image.copyData(raster); + return bimage; + } + + /** + * This method returns a buffered image with the contents of an image. + * Taken from http://www.exampledepot.com/egs/java.awt.image/Image2Buf.html + * @param image Image to be converted + * @return a buffered image with the contents of the specified image + */ + public static BufferedImage toBufferedImage(Image image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + // This code ensures that all the pixels in the image are loaded + image = new ImageIcon(image).getImage(); + // Determine if the image has transparent pixels + boolean hasAlpha = hasAlpha(image); + + // Create a buffered image with a format that's compatible with the + // screen + BufferedImage bimage; + GraphicsEnvironment ge = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + try { + // Determine the type of transparency of the new buffered image + int transparency = Transparency.OPAQUE; + if (hasAlpha) { + transparency = Transparency.TRANSLUCENT; + } + // Create the buffered image + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + bimage = gc.createCompatibleImage( + image.getWidth(null), image.getHeight(null), transparency); + } catch (HeadlessException e) { + // The system does not have a screen + bimage = null; + } + if (bimage == null) { + // Create a buffered image using the default color model + int type = BufferedImage.TYPE_INT_RGB; + if (hasAlpha) { + type = BufferedImage.TYPE_INT_ARGB; + } + bimage = new BufferedImage( + image.getWidth(null), image.getHeight(null), type); + } + // Copy image to buffered image + Graphics g = bimage.createGraphics(); + // Paint the image onto the buffered image + g.drawImage(image, 0, 0, null); + g.dispose(); + return bimage; + } + + public static Shape clone(Shape shape) { + if (shape == null) { + return null; + } + Shape clone; + if (shape instanceof Line2D) { + clone = (shape instanceof Line2D.Float) ? + new Line2D.Float() : new Line2D.Double(); + ((Line2D) clone).setLine((Line2D) shape); + } else if (shape instanceof Rectangle) { + clone = new Rectangle((Rectangle) shape); + } else if (shape instanceof Rectangle2D) { + clone = (shape instanceof Rectangle2D.Float) ? + new Rectangle2D.Float() : new Rectangle2D.Double(); + ((Rectangle2D) clone).setRect((Rectangle2D) shape); + } else if (shape instanceof RoundRectangle2D) { + clone = (shape instanceof RoundRectangle2D.Float) ? + new RoundRectangle2D.Float() : new RoundRectangle2D.Double(); + ((RoundRectangle2D) clone).setRoundRect((RoundRectangle2D) shape); + } else if (shape instanceof Ellipse2D) { + clone = (shape instanceof Ellipse2D.Float) ? + new Ellipse2D.Float() : new Ellipse2D.Double(); + ((Ellipse2D) clone).setFrame(((Ellipse2D) shape).getFrame()); + } else if (shape instanceof Arc2D) { + clone = (shape instanceof Arc2D.Float) ? + new Arc2D.Float() : new Arc2D.Double(); + ((Arc2D) clone).setArc((Arc2D) shape); + } else if (shape instanceof Polygon) { + Polygon p = (Polygon) shape; + clone = new Polygon(p.xpoints, p.ypoints, p.npoints); + } else if (shape instanceof CubicCurve2D) { + clone = (shape instanceof CubicCurve2D.Float) ? + new CubicCurve2D.Float() : new CubicCurve2D.Double(); + ((CubicCurve2D) clone).setCurve((CubicCurve2D) shape); + } else if (shape instanceof QuadCurve2D) { + clone = (shape instanceof QuadCurve2D.Float) ? + new QuadCurve2D.Float() : new QuadCurve2D.Double(); + ((QuadCurve2D) clone).setCurve((QuadCurve2D) shape); + } else if (shape instanceof Path2D.Float) { + clone = new Path2D.Float(shape); + } else { + clone = new Path2D.Double(shape); + } + return clone; + } + + private static class FontExpressivenessComparator implements Comparator { + private static final int[] STYLES = { + Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD | Font.ITALIC + }; + public int compare(Font font1, Font font2) { + if (font1 == font2) { + return 0; + } + Set variantNames1 = new HashSet<>(); + Set variantNames2 = new HashSet<>(); + for (int style : STYLES) { + variantNames1.add(font1.deriveFont(style).getPSName()); + variantNames2.add(font2.deriveFont(style).getPSName()); + } + if (variantNames1.size() < variantNames2.size()) { + return 1; + } else if (variantNames1.size() > variantNames2.size()) { + return -1; + } + return font1.getName().compareTo(font2.getName()); + } + } + + private static final FontExpressivenessComparator FONT_EXPRESSIVENESS_COMPARATOR = + new FontExpressivenessComparator(); + + private static boolean isLogicalFontFamily(String family) { + return (Font.DIALOG.equals(family) || + Font.DIALOG_INPUT.equals(family) || + Font.SANS_SERIF.equals(family) || + Font.SERIF.equals(family) || + Font.MONOSPACED.equals(family)); + } + + /** + * Try to guess physical font from the properties of a logical font, like + * "Dialog", "Serif", "Monospaced" etc. + * @param logicalFont Logical font object. + * @param testText Text used to determine font properties. + * @return An object of the first matching physical font. The original font + * object is returned if it was a physical font or no font matched. + */ + public static Font getPhysicalFont(Font logicalFont, String testText) { + String logicalFamily = logicalFont.getFamily(); + if (!isLogicalFontFamily(logicalFamily)) { + return logicalFont; + } + + final TextLayout logicalLayout = + new TextLayout(testText, logicalFont, FONT_RENDER_CONTEXT); + + // Create a list of matches sorted by font expressiveness (in descending order) + Queue physicalFonts = + new PriorityQueue<>(1, FONT_EXPRESSIVENESS_COMPARATOR); + + Font[] allPhysicalFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font physicalFont : allPhysicalFonts) { + String physicalFamily = physicalFont.getFamily(); + // Skip logical fonts + if (isLogicalFontFamily(physicalFamily)) { + continue; + } + + // Derive identical variant of physical font + physicalFont = physicalFont.deriveFont( + logicalFont.getStyle(), logicalFont.getSize2D()); + TextLayout physicalLayout = + new TextLayout(testText, physicalFont, FONT_RENDER_CONTEXT); + + // Compare various properties of physical and logical font + if (physicalLayout.getBounds().equals(logicalLayout.getBounds()) && + physicalLayout.getAscent() == logicalLayout.getAscent() && + physicalLayout.getDescent() == logicalLayout.getDescent() && + physicalLayout.getLeading() == logicalLayout.getLeading() && + physicalLayout.getAdvance() == logicalLayout.getAdvance() && + physicalLayout.getVisibleAdvance() == logicalLayout.getVisibleAdvance()) { + // Store matching font in list + physicalFonts.add(physicalFont); + } + } + + // Return a valid font even when no matching font could be found + if (physicalFonts.isEmpty()) { + return logicalFont; + } + + return physicalFonts.poll(); + } + + public static Font getPhysicalFont(Font logicalFont) { + return getPhysicalFont(logicalFont, FONT_TEST_STRING); + } + + public static BufferedImage getAlphaImage(BufferedImage image) { + WritableRaster alphaRaster = image.getAlphaRaster(); + int width = image.getWidth(); + int height = image.getHeight(); + + if (alphaRaster == null) { + BufferedImage opaqueAlphaImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + Graphics g = opaqueAlphaImage.getGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, width, height); + return opaqueAlphaImage; + } + + ColorModel cm; + WritableRaster raster; + // TODO Handle bitmap masks (work on ImageDataStream is necessary) + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); + int[] bits = {8}; + cm = new ComponentColorModel(colorSpace, bits, false, true, + Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + raster = cm.createCompatibleWritableRaster(width, height); + + BufferedImage alphaImage = new BufferedImage(cm, raster, false, null); + + int[] alphaValues = new int[image.getWidth()*alphaRaster.getNumBands()]; + for (int y = 0; y < image.getHeight(); y++) { + alphaRaster.getPixels(0, y, image.getWidth(), 1, alphaValues); + // FIXME Don't force 8-bit alpha channel (see TODO above) + if (image.getTransparency() == BufferedImage.BITMASK) { + for (int i = 0; i < alphaValues.length; i++) { + if (alphaValues[i] > 0) { + alphaValues[i] = 255; + } + } + } + alphaImage.getRaster().setPixels(0, y, image.getWidth(), 1, alphaValues); + } + + return alphaImage; + } + + public static boolean equals(Shape shapeA, Shape shapeB) { + PathIterator pathAIterator = shapeA.getPathIterator(null); + PathIterator pathBIterator = shapeB.getPathIterator(null); + + if (pathAIterator.getWindingRule() != pathBIterator.getWindingRule()) { + return false; + } + double[] pathASegment = new double[6]; + double[] pathBSegment = new double[6]; + while (!pathAIterator.isDone()) { + int pathASegmentType = pathAIterator.currentSegment(pathASegment); + int pathBSegmentType = pathBIterator.currentSegment(pathBSegment); + if (pathASegmentType != pathBSegmentType) { + return false; + } + for (int segmentIndex = 0; segmentIndex < pathASegment.length; segmentIndex++) { + if (pathASegment[segmentIndex] != pathBSegment[segmentIndex]) { + return false; + } + } + + pathAIterator.next(); + pathBIterator.next(); + } + // When the iterator of shapeA is done and shapeA equals shapeB, + // the iterator of shapeB must also be done + if (!pathBIterator.isDone()) { + return false; + } + return true; + } +} diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/ImageDataStream.java b/Java/src/de/erichseifert/vectorgraphics2d/util/ImageDataStream.java new file mode 100644 index 00000000..6e4a7728 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/ImageDataStream.java @@ -0,0 +1,156 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.Queue; + +public class ImageDataStream extends InputStream { + public enum Interleaving { + SAMPLE, + ROW, + WITHOUT_ALPHA, + ALPHA_ONLY + } + + private final BufferedImage image; + private final int width; + private final int height; + private final Interleaving interleaving; + private final Raster raster; + private final boolean opaque; + + private final Queue byteBuffer; + private final int[] sampleValues; + private final int[] sampleSizes; + private int x; + private int y; + //private byte currentBit; + + public ImageDataStream(BufferedImage image, Interleaving interleaving) { + this.image = image; + this.interleaving = interleaving; + + width = image.getWidth(); + height = image.getHeight(); + x = -1; + y = 0; + + Raster alphaRaster = image.getAlphaRaster(); + if (interleaving == Interleaving.ALPHA_ONLY) { + raster = alphaRaster; + } else { + raster = image.getRaster(); + } + opaque = alphaRaster == null; + + byteBuffer = new LinkedList<>(); + sampleValues = new int[raster.getNumBands()]; + sampleSizes = raster.getSampleModel().getSampleSize(); + } + + public BufferedImage getImage() { + return image; + } + + + public Interleaving getInterleaving() { + return interleaving; + } + + @Override + public int read() { + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + if (!nextSample()) { + return -1; + } + int bands = sampleValues.length; + if (interleaving == Interleaving.WITHOUT_ALPHA || + interleaving == Interleaving.ALPHA_ONLY) { + if (interleaving == Interleaving.WITHOUT_ALPHA && !opaque) { + // Ignore alpha band + bands--; + } + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + if (opaque) { + for (int band = 0; band < bands; band++) { + bufferSampleValue(band); + } + } else { + for (int band = 0; band < bands; band++) { + // Fix order to be ARGB instead of RGBA + if (band == 0) { + bufferSampleValue(bands - 1); + } else { + bufferSampleValue(band - 1); + } + } + } + } + if (!byteBuffer.isEmpty()) { + return byteBuffer.poll(); + } else { + return -1; + } + } + } + + private void bufferSampleValue(int band) { + if (sampleSizes[band] < 8) { + // TODO Handle data with sample sizes smaller than 1 byte + int byteValue = sampleValues[band] & 0xFF; + byteBuffer.offer(byteValue); + } else { + int byteCount = sampleSizes[band]/8; + for (int i = byteCount - 1; i >= 0; i--) { + int byteValue = (sampleValues[band] >> i*8) & 0xFF; + byteBuffer.offer(byteValue); + } + } + } + + private boolean nextSample() { + if (interleaving == Interleaving.SAMPLE || interleaving == Interleaving.WITHOUT_ALPHA) { + x++; + if (x >= width) { + x = 0; + y++; + } + } + if (x < 0 || x >= width || y < 0 || y >= height) { + return false; + } else { + raster.getPixel(x, y, sampleValues); + return true; + } + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/LineWrapOutputStream.java b/Java/src/de/erichseifert/vectorgraphics2d/util/LineWrapOutputStream.java new file mode 100644 index 00000000..a7cff28a --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/LineWrapOutputStream.java @@ -0,0 +1,58 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class LineWrapOutputStream extends FilterOutputStream { + public static final String STANDARD_EOL = "\r\n"; + + private final int lineWidth; + private final byte[] eolBytes; + private int written; + + public LineWrapOutputStream(OutputStream sink, int lineWidth, String eol) { + super(sink); + this.lineWidth = lineWidth; + this.eolBytes = eol.getBytes(); + if (lineWidth <= 0) { + throw new IllegalArgumentException("Width must be at least 0."); + } + } + + public LineWrapOutputStream(OutputStream sink, int lineWidth) { + this(sink, lineWidth, STANDARD_EOL); + } + + @Override + public void write(int b) throws IOException { + if (written == lineWidth) { + out.write(eolBytes); + written = 0; + } + out.write(b); + written++; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/PageSize.java b/Java/src/de/erichseifert/vectorgraphics2d/util/PageSize.java new file mode 100644 index 00000000..34050c26 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/PageSize.java @@ -0,0 +1,99 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +package de.erichseifert.vectorgraphics2d.util; + +import java.awt.geom.Rectangle2D; + +/** + *

    Class that represents a page with a specified origin and size. + * The class is immutable and can be initialized with coordinates and + * dimensions or only dimensions:

    + *
    PageSize a3 = new PageSize(0.0, 0.0, 297.0, 420.0);
    + *PageSize a4 = new PageSize(210.0, 297.0);
    + * + *

    For convenience the class contains static constants for common page + * sizes:

    + *
    PageSize a4 = PageSize.A4;
    + *PageSize letter = PageSize.LETTER;
    + */ +public class PageSize { + private static final double MM_PER_INCH = 2.54; + + public static final PageSize A3 = new PageSize(297.0, 420.0); + public static final PageSize A4 = new PageSize(210.0, 297.0); + public static final PageSize A5 = new PageSize(148.0, 210.0); + public static final PageSize LETTER = new PageSize(8.5*MM_PER_INCH, 11.0*MM_PER_INCH); + public static final PageSize LEGAL = new PageSize(8.5*MM_PER_INCH, 14.0*MM_PER_INCH); + public static final PageSize TABLOID = new PageSize(11.0*MM_PER_INCH, 17.0*MM_PER_INCH); + public static final PageSize LEDGER = TABLOID.getLandscape(); + + private final double x; + private final double y; + private final double width; + private final double height; + + public PageSize(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public PageSize(double width, double height) { + this(0.0, 0.0, width, height); + } + + public PageSize(Rectangle2D size) { + this(size.getX(), size.getY(), size.getWidth(), size.getHeight()); + } + + public PageSize getPortrait() { + if (width <= height) { + return this; + } + return new PageSize(x, y, height, width); + } + + public PageSize getLandscape() { + if (width >= height) { + return this; + } + return new PageSize(x, y, height, width); + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } +} + diff --git a/Java/src/de/erichseifert/vectorgraphics2d/util/package-info.java b/Java/src/de/erichseifert/vectorgraphics2d/util/package-info.java new file mode 100644 index 00000000..b69d69d8 --- /dev/null +++ b/Java/src/de/erichseifert/vectorgraphics2d/util/package-info.java @@ -0,0 +1,25 @@ +/* + * VectorGraphics2D: Vector export for Java(R) Graphics2D + * + * (C) Copyright 2010-2019 Erich Seifert , + * Michael Seifert + * + * This file is part of VectorGraphics2D. + * + * VectorGraphics2D is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VectorGraphics2D 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with VectorGraphics2D. If not, see . + */ +/** + * Utility classes for common data or graphics operations. + */ +package de.erichseifert.vectorgraphics2d.util; diff --git a/LGLviewshortcuts.png b/LGLviewshortcuts.png new file mode 100644 index 00000000..c5857fa1 Binary files /dev/null and b/LGLviewshortcuts.png differ diff --git a/README.md b/README.md index b67bcaa9..a6487a8f 100644 --- a/README.md +++ b/README.md @@ -1,142 +1,102 @@ -# Slightly modified LGL +# LGL - Large Graph Layout -[![Build Status](https://teamcity.eskaduren.se/app/rest/builds/buildType:(id:Phdeis_BasicRunLgl)/statusIcon.svg)](https://teamcity.eskaduren.se) ![Github build all](https://github.com/flindeberg/LGL/workflows/Build%20and%20install%20CI/badge.svg) ![Github test all](https://github.com/flindeberg/LGL/workflows/Testrun%20CI/badge.svg) +![Github build all](https://github.com/flindeberg/LGL/workflows/Build%20and%20install%20CI/badge.svg) ![Github test all](https://github.com/flindeberg/LGL/workflows/Testrun%20CI/badge.svg) All files distributed with LGL fall under the terms of the GNU General Public License, and are copyright (c) 2002, 2003 Alex Adai. -Changes done in this fork copyright (c) 2019, 2020 Fredrik Lindeberg +Changes and updates copyright (c) 2004-2022 Barrett Lyon -LGL on the web at: http://www.opte.org/lgl/ (defunct) +LGL on the web at: http://www.opte.org/ -Much thanks to the Marcotte lab for testing. If you use this in your research, please cite (if possible): -- Lindeberg, Fredrik. “Coordinating the Internet.” Licentiate Thesis, Linköping University, 2019. http://urn.kb.se/resolve?urn=urn:nbn:se:liu:diva-161812. +- Lyon, Barrett. "The Opte Project." Mapping the Internet, Self, 2003-2022. http://www.opte.org. - Adai, Alex T., Shailesh V. Date, Shannon Wieland, and Edward M. Marcotte. “LGL: Creating a Map of Protein Function with an Algorithm for Visualizing Very Large Biological Networks.” Journal of Molecular Biology 340, no. 1 (2004): 179–90. https://doi.org/10.1016/j.jmb.2004.04.047. -As a word of warning, the purpose of this repository is currently to show code used for my thesis for review purposes, not primarily as a nice and sharable code base. + +Note: Most of the Perl contained in this package is no longer maintained. The Java and C++ components are "mostly" modernized. + # Example output -![Image of the Internet](https://raw.githubusercontent.com/flindeberg/LGL/master/resources/images/internet_2016.png) +![Holding Space](https://raw.githubusercontent.com/clairemcwhite/clairemcwhite.github.io/master/images/homology_graph.jfif) + +Protein Homology Graph, Edward Marcotte and Alex Adai, MoMA + +![The Internet 2003](http://content.opte.org/content/opte/maps/static/1069646562.LGL.2D.700x700.png) + +Internet using traceroute vs BGP in 2003, Barrett Lyon, MoMA + -An example of the Internet as generated by data from 2016. The grapher can in theory handle any kind of data in ncol-format, although all the helper scripts I have added focus on parsing and handling BGP- and similar data. # Requirements A heap of stuff, in no particular order: - A C++ compiler -- - Boost library required (I should have fixed version issues, but I have not future proofed it) + - Boost library required (I should have fixed version issues, but I have not future proofed it) - bgpdump (https://bitbucket.org/ripencc/bgpdump/wiki/Home) -- - Seems broken in Debian distros, so compile from source if needed +- Also see bgpscanner (Works with threads and is faster than bgpdump) + - https://blog.apnic.net/2018/11/29/new-mrt-bgp-reader-six-times-faster-than-its-predecessors/ - perl (5+, I think) -- Java (8 seems to work) +- Java (openjdk version "11.0.16" 2022-07-19) - Xserver installed for graphical tools (works well under WSL 2 in Windows) + - Graphical tools also work on anything with Java. - Python 3 (there are bash scripts lying around as well, but the python scripts are 3x faster) + - Only used for example scripts -# Installation +# Additional reading -Use the Makefil, i.e. +- User guide to LGL: + - http://clairemcwhite.github.io/lgl-guide/ +- Getting up to speed on Internet routing: + - http://networkingbodges.blogspot.com/2019/04/a-real-full-internet-table-in-lab.html + - https://www.noction.com/blog/as-path-and-as-path-prepending - prompt$ make - prompt$ make install # local install in $(PROJECTDIR)/bin -should do the trick in the root directory (feel free to improve the magic -`setup.pl -i` script which does a lot of suspect lifting). +# Installation -# Making Internet graphs -If your intention is to graph custom stuff, just hack away. Below is how you quite easily -can make graphs from bgp-dumps. You need a separate folder for each project, due to design -in the original LGL library. Below follows an example for a graph for a 2000 Internet. Takes -around 10 minutes on a fairly modern computer (8 threads or so) with a decent Internet connection. +## Compling LGL C++ components -The oneliner which creates a graph, including bootstrapping, from 2000-09-01: + prompt$ setup.pl -i - prompt$ cd scripts/ - prompt$ ./creategraphfromdate.sh 2000 09 - prompt$ # doing magic, and creating a graph - prompt$ # arguments to creategraphfromdate are year and month - prompt$ # and the scipt lacks proper error handling + This script which does a lot of suspect lifting. -The same thing but step by step (if above fails): - prompt$ cd scripts/ - prompt$ ./create_run.sh internet_2000 - prompt$ cd ../testrun/internet_2000 - prompt$ wget http://data.ris.ripe.net/rrc00/2000.09/bview.20000901.0610.gz - prompt$ ./bootstrap.sh bview.20000901.0610.gz - prompt$ # doing magic, and creating a graph - prompt$ # by default generating a 2400x2400 png (change run.sh for different resolution) - prompt$ # should be a 'internet_2001.png' in 'testrun/internet_2001' if all went well - - Also possible: - - prompt$ cd scripts/ - prompt$ ./creategraphfromurl.sh http://data.ris.ripe.net/rrc00/2000.09/bview.20000901.0610.gz - prompt$ # wait for magic, by default generating a 2400x2400 png (change run.sh for different resolution) - prompt$ # should be a 'view.20000901.0610.png' in 'testrun/bview.20000901.0610' if all went well - -Replace the bview-file with a more recent one for a larger and newer network network. Coloring is -set in `perls/colorEdgesBasedOnLevel.pl`, currently a mix of greenish and bluish tints going on white -at the edges. +This will compile 2D and 3D versions of LGL and put the resulting binaries +in the ./bin directory. Afterwards you can move them whereever you want. -A short disclaimer; the graph is only as good as your data. The `bootstrap` script works for generating interesting graphs. Are they 100% correct? I don't know, you are welcome to check and improve the code! There might be BGP-quirks I do not know of, even though I catch the vast majority of bgp announcements. -# Additional reading +NOTE: setup.pl has been updated to locate boost, however you may need + to direct gcc on where to find the includes if the automated + detection does not work: -User guide to LGL, helped me figure out some important things: -http://clairemcwhite.github.io/lgl-guide/ + env CPLUS_INCLUDE_PATH=/usr/local/include ./setup.pl -i -The project I forked from (seemingly dead, left a pull request): -https://github.com/TheOpteProject/LGL/ -Getting up to speed on Internet routing: -http://networkingbodges.blogspot.com/2019/04/a-real-full-internet-table-in-lab.html -https://www.noction.com/blog/as-path-and-as-path-prepending +## Compile Java jar files -# Old readme -## Table of contents +Use the gmake Makefile, i.e. - 0 Before compiling! - I Setup and Installation - II Other files that come with LGL -III Expanding LGL -IV What's new for 2.0 + prompt$ make + prompt$ make install # local install in $(PROJECTDIR)/bin -## Before compiling! -Firstly, LGL will probably only compile with the GNU compilers. It was tested -on FreeBSD 9.x and CentOS, but it should compile OK on other Linux -distributions. For other operating systems you are on your own. Good Luck :-) +## Configure Perl +(If you intend to use these scrips) You must have the following Perl modules in your @INC path to run LGL: -ParseConfigFile.pm -LGLFormatHandler.pm + ParseConfigFile.pm + LGLFormatHandler.pm These files are in the ./perls directory. You don't have to know anything about these modules, and you won't have to use them directly but lgl.pl will call them. -## Setup and installation - -To compile LGL change to the same directory as setup.pl and type: - - prompt$ ./setup.pl -i - -This will compile 2D and 3D versions of LGL and put the resulting binaries -in the ./bin directory. Afterwards you can move them whereever you want. - - -NOTE: setup.pl has been updated to locate boost, however you may need - to direct gcc on where to find the includes if the automated - detection does not work: - - env CPLUS_INCLUDE_PATH=/usr/local/include ./setup.pl -i After all is compiled and done you can run LGL by the driver script lgl.pl as: @@ -172,7 +132,7 @@ the root bin directory of all the lgl executables (This might be program won't run until it is set correctly. -## Other files that come with LGL +# Other files that come with LGL A list of important files in the perls dir: @@ -187,15 +147,92 @@ Other files might be included as well, but they are not necessary for LGL. Their documentation will be added here in the near future, or they may not be carried in the future. -### Other Files: +## lglayout2D + +This is the core of LGL and generates the coords file for image generation. + +Usage: ./lgl-exparmental-label/bin/lglayout2D [-x InitPositionFile] [-a AnchorsFile] + [-t ThreadCount] [-m InitMassFile] [-i IterationMax] + [-s] [-r nbhdRadius] [-T timeStep] [-S nodeSizeRadius] + [-k casualSpringConstant] [-s specialSpringConstant] + [-e] [-l] [-y] [-q EQ Distance] [-u placementDistance] + [-E ellipseFactors] [-v placementRadius] [-L] nodeFile.lgl + + + -[mx] A file that has the node id followed by + the initial values. + + -t The number of threads to spawn. + This is capped by the processor count. + + -i The maximum number of iterations. + + -r The neighborhood radius for each particle. It + defines the interaction range for casual (generally + repulsive) interactions + + -T The time step for each iteration + + -S The 'radius' of each node. + + -M The 'mass' of each node. + + -R The radius of the outer perim. -LGLView.jar - A JAVA 2D viewer for looking at the output of lgl.pl. The output of the layout programs is just a set of coordinates. For looking at 2D -coordinates use lglview.jar See the web page http://www.opte.org/lgl for -usage and other info. + -W The write interval. -ImageMaker.jar - A JAVA 2D image output tool. For more detail visit http://www.opte.org/lgl/ + -z Root node you want to use. + -l Write out the edge level map. + + -e Output the mst used. + + -O Use original weights. + + -y Layout the tree only. + + -I Don't show layout progress, be quiet (kinda) + + -q Equilibrium distance. + + -E Ellipse factors. (Force the layout to be more of an ellipse -E 1x1.2) + + -u Placement distance is the distance you want + the next level to be placed with respect to + the previous level. If this float value is not + given a formula calculates the placement distance. + + -v Placement radius is a measure of the placement density + + -L Place the leafs close by. This applies to trees more than + graphs. Setting this option will place the child vertices very + near the parent vertex if all of its children have none themselves. + + -o Read a previously created coordinates file to start processing level processing. This technique is used for animations. + + + +## ImageMaker +### A JAVA 2D image output tool. + +Generating Static Images using LGLLib.jar + +Runtime Example: + + java -Djava.awt.headless=true -Xmx20000m -Xms20000m -cp ./LGL-Master/Java/jar/LGLLib.jar ImageMaker.GenerateImages run.lgl run.coords -c run.colors -s 0.01 -l run.labels + + -c + -s Scale for labels + -l + -M x,y (max of x and y to control scale and zoom. Not required) + -m x,y (min of x and y to control scale and zoom. Not required) + -a center (not required, align the mean of the image to the center of the window) + +To use the -M and -m features you can get the min and max values from rendering the largest +graph in your series. You can use the largest graph's min/max values on the smallest series +to keep perspective rather than a fit-to-screen zoom. + Looking at the huge PNG (100k x 100k pixels) java.awt.image.Raster: The maximum width x height has to be less than Integer.MAX_VALUE (2147483647) so the maximum square image is 46340 x 46340. Note also that such images will @@ -203,39 +240,288 @@ need a lot of RAM since Java's BufferedImage's pixels are hold in memory. An example of a larger output would be: - java -Xms1G -Xmx5G -jar ImageMaker.jar 29200 29200 + java -Djava.awt.headless=true -Xmx5G -Xms5G -cp ./LGL-Master/Java/jar/LGLLib.jar ImageMaker.GenerateImages 29200 29200 + + +## LGLView +### A JAVA 2D viewer for looking and reviewing .lgl, .coord, .color, and .labels files. + +coordinates use LGLLib.jar + + java -Xmx2G -Xms2G -cp ./LGL-master/Java/jar/LGLLib.jar Viewer2D.Viewer2D + +Shortcuts +![Holding Space](https://github.com/TheOpteProject/LGL/blob/master/LGLviewshortcuts.png) + - LGLView.jar - The full package that combines LGLView and ImageMaker. +## LGLView.jar +### The full package that combines LGLView and ImageMaker. - Java - Directory and source code of all JAVA programs. See README in the JAVA dir. +## Java +### Directory and source code of all JAVA programs. See README in the JAVA dir. -## Expanding LGL + + +# File Formats: +(Thank you to Claire McWhite for major parts of this tutorial) + +## Input format (.lgl) + +The input format to LGL is called .ncol, which is just a space separated list of two connected nodes with an optional third column of weight. + + $ cat example.lgl + node1 node2 [optional weight] + Key points for formatting the input .ncol + + Each line must be unique + A node may connect to many other nodes + A node cannot connect to itself + If a line is B-A, there cannot also be a line A-B + There can’t be blank lines + There can’t be blanks in any column + No header line + node1 node2 + node1 node3 # OK + node1 node2 # Will cause error + node1 node1 # Will cause error + node2 node1 # Will cause error + node3 # Will cause error + # Trailing blank line will cause error + +## Coloring format (.colors) + +LGL allows you to color both nodes and edges. In order to color edges, each pairwise edge must have an R G B value. To color individual nodes, each node must have an R G B value. RGB values must be scaled to one 1, so just divide each number of an RGB value by 255. The rules for formatting an .ncol file apply here too, i.e. no blanks, no empty lines, no redundancy, etc. + +LGL also now supports colors in hex values and is backward compatible with the previous RGB format. + + $ cat example.edge.colors + node1 node2 1.0 0.5 0.0 + node3 node4 0.0 1.0 0.8 + node5 node6 0.1 0.1 1.0 + + or + + $ cat example2.edge.colors + node1 node2 FFFFFF FFFFFF FFFFFF + node3 node4 0.0 1.0 0.8 + node5 node6 0.1 0.1 1.0 + + + $ cat example.vertex.colors + node1 1.0 0.8 0.0 + node2 1.0 0.5 0.0 + node3 0.2 0.1 0.8 + node4 0.0 1.0 0.8 + node5 0.6 0.5 0.5 + node6 0.1 0.1 1.0 + + +## The new labels file format (.labels) + +LGL now supports labels! It's a flat file with single line entries for the configuration of each label with a \n at the end. + +Pixel sizes cannot be in decimal or fractions + +Colors will be referenced by their hex values. 000000-111111 (Do not include the #) + +NULL values will delete the segment of the label: shape, line, text1, text2 + +![Holding Space](http://content.opte.org/content/labels-example.jpg) + +Example of image generated with labels. + +## Format: + + nodename, + (name of the node we want to center the shape around) + shape, + (name of shape: circle or square) + shape_size, + (size of the shape in pixels from center to edge?) + shape_border_with, + (width of the shape in px) + shape_border_color, + (color used for the shape border) + shape_fill_color, + (color used to fill the shape) + shape_fill_opacity, + (opacity for the fill of the shape) + line_size, + (width of line in px) + line_length, + (length of line in px) + line_angle, + (direction of the line off the shape edge) + line_color, + (color of the line off the shape) + top_text_ttf, + (full path filename for ttf font) + top_text_size, + (text size) + top_text_color, + (text color for upper text) + top_bg_fill_color + (color used for text background) + bottom_text_ttf, + (full path filename for ttf font) + bottom_text_size, + (text size) + bottom_text_color, + (text color for upper text) + bottom_bg_fill_color + (color used for text background) + top_text + (text string for top label) + Bottom_text + (text string for bottom label) + + +Example of a label in the input config file: + + + 174,circle,20,2,000000,000000,100,5,25,30,000000,file.ttf,25,FFFFFF,000000,file.ttf,50,FFFFFF,000000,COGENT COMMUNICATIONS,GLOBAL NETWORK + + +Command line example: + + java -Djava.awt.headless=true -Xmx20000m -Xms20000m -cp ./LGL-master/Java/jar/LGLLib.jar ImageMaker.GenerateImages 5000 5000 -c -l -s + + + +# An LGL workflow + +## Barrett Lyon's workflow: +For Opte we parse the output of BGP into an .ncol (.lgl) file format, a .color file, and a .label file. The lgl file is read into lglayout2D, which creates the .coords output file. These combined are read by the ImageMaker Java application to output an image. + +To create an animation, each step in the data set is incremental in time. Thus to generate the next frame in the animation, the -o option is used to read in the previous .coords file. The last file is used to set up the landscape and processes the changes from one dataset to the next. + +Once all the images are complete, they can be combined as an animation using FFMpeg. + +Data parsing -> lglayout2d -> ImakeMaker -> Animation + + +## Here's another workflow from Claire McWhite: + +This is any example to make a network with colored nodes and edges. I would begin by making a file of all pairwise edges and their associated traits. It can be difficult to keep .ncol and .color files in sync, and so it will cause fewest headaches to begin with one file containing all the information to create both. + + $ echo "Nodes and traits" + $ cat homology.txt + node1 node2 source score rank species1 species2 + protein1 protein2 blastp 150 1 mouse human + protein3 protein4 blastp 50 2 wheat rat + protein2 protein5 hmmscan 60 3 human human + Then take the first two columns (minus the header) to create an .ncol file. This is the file used to layout the graph + + $ echo "Get node columns, remove header" + $ awk '{print $1, $2}' homology.txt | awk '{if(NR>1)print}' > homology.ncol + $ cat homology.ncol + protein1 protein2 + protein3 protein4 + protein2 protein5 + +Then choose a trait, and create a edge.colors file. I generally select the first two columns, and a trait to color by, then just use sed to replace the trait values with the RGB value I want to color that type of edge by. + +In this file, we want to color all edges predicted with the algorithm hmmscan red, and all edges found with blastp blue. + + $ echo "Get node columns and trait column" + $ awk '{print $1, $2, $3}' homology.txt | awk '{if(NR>1)print}' > homology_alg.colors.tmp + + $ echo "Replace hmmscan trait with RGB value + $ sed -i 's/hmmscan/0 1 0/g' homology_alg.colors.tmp + + $ echo "Replace blastp trait with RGB value + $ sed 's/blastp/0 0 0/g' homology_alg.colors.tmp > homology_algorithm.edge.colors + + $ cat homology_algorithm.edge.colors + protein1 protein2 0 0 0 + protein3 protein4 0 0 0 + protein2 protein5 0 1 0 + +We could also color each node by some trait. In this file format, each vertex must have an associated RGB value. In this case, we want to color every human protein red, and proteins from every other species blue. + + $ echo "Get first column of nodes and species" + $ awk '{print $1, $6}' homology.txt | awk '{if(NR>1)print}'> vertex1_species.tmp + + $ Get second column of nodes and species" + $ awk '{print $2, $7}' homology.txt | awk '{if(NR>1)print}'> vertex2_species.tmp + + $ Get unique nodes" + $ cat vertex1_species.tmp vertex2_species.tmp | sort -u > homology_human.vertex.colors.tmp + + $ Color human nodes red" + $ sed -i 's/human/1 0 0/' homology_human.vertex.colors.tmp + + $ Color any other node blue" + $ sed 's/mouse\|wheat\|rat/0 0 1/' homology_human.vertex.colors.tmp > homology_human.vertex.colors + + $ cat homology.human.vertex.colors + protein1 0 0 1 + protein3 0 0 1 + protein2 1 0 0 + protein4 0 0 1 + protein5 1 0 0 + Running + + +# Pre-seeding animation technique + +The Opte Project was able to use lglayout2D to create a full animation in 10k using this new pre-seeding technique: + + ./LGL-master/bin/lglayout2D -D -t 56 -x -o -E1x1.2 run.lgl + +LGL will read the previous file and start with those coordinates rather than starting from scratch. This technique allows nodes to stay in nearly the same place creating another frame generated from the previous. + + +# Expanding LGL The most obvious way to expand LGL is to add support for your type of edge file to LGLFormatHandler.pm. Just add a method to read in your file type, update the 'loadFromFile' method to recognize your file suffix, and that should be it. -Let me know of any source code contributions that would make LGL more suitable -and usable so I can add the code in! +# Example of Internet graphs -## What's new for 2.0 +![Image of the Internet](https://raw.githubusercontent.com/flindeberg/LGL/master/resources/images/internet_2016.png) -LGL-2.0 is the first new release of LGL in years. It's the first release by -the new LGL team at the Opte Project. LGL was the baby of Alex Adia and -he's been too busy to update things, now we're involved in bringing the -code up-to-date. +An example of the Internet as generated by data from 2016. Lindeberg, Fredrik -2.0 changes include: -- The code has been ported to boost-1.55 -- Now compiles on modern operating systems -- LGLView.jar has been updated to work with Java 1.6 -- ImageMaker.jar has been released with the ability to do large resolution (46340 x 46340 pixel output). -- UI fixes for lglview.jar -- The original code uses Jama, the old classes were included with the software package, we've now changed that to use the JAR from Jama RI. +These scrips and changes are copyright (c) 2019, 2020 Fredrik Lindeberg -Cheers the LGL team! -lgl@opte.org +If your intention is to graph custom stuff, just hack away. Below is how you quite easily +can make graphs from bgp-dumps. You need a separate folder for each project, due to design +in the original LGL library. Below follows an example for a graph for a 2000 Internet. Takes +around 10 minutes on a fairly modern computer (8 threads or so) with a decent Internet connection. +The oneliner which creates a graph, including bootstrapping, from 2000-09-01: + + prompt$ cd scripts/ + prompt$ ./creategraphfromdate.sh 2000 09 + prompt$ # doing magic, and creating a graph + prompt$ # arguments to creategraphfromdate are year and month + prompt$ # and the scipt lacks proper error handling + +The same thing but step by step (if above fails): + + prompt$ cd scripts/ + prompt$ ./create_run.sh internet_2000 + prompt$ cd ../testrun/internet_2000 + prompt$ wget http://data.ris.ripe.net/rrc00/2000.09/bview.20000901.0610.gz + prompt$ ./bootstrap.sh bview.20000901.0610.gz + prompt$ # doing magic, and creating a graph + prompt$ # by default generating a 2400x2400 png (change run.sh for different resolution) + prompt$ # should be a 'internet_2001.png' in 'testrun/internet_2001' if all went well + + Also possible: + + prompt$ cd scripts/ + prompt$ ./creategraphfromurl.sh http://data.ris.ripe.net/rrc00/2000.09/bview.20000901.0610.gz + prompt$ # wait for magic, by default generating a 2400x2400 png (change run.sh for different resolution) + prompt$ # should be a 'view.20000901.0610.png' in 'testrun/bview.20000901.0610' if all went well + +Replace the bview-file with a more recent one for a larger and newer network network. Coloring is +set in `perls/colorEdgesBasedOnLevel.pl`, currently a mix of greenish and bluish tints going on white +at the edges. + +A short disclaimer; the graph is only as good as your data. The `bootstrap` script works for generating interesting graphs. Are they 100% correct? I don't know, you are welcome to check and improve the code! There might be BGP-quirks I do not know of, even though I catch the vast majority of bgp announcements. diff --git a/doc/options.txt b/doc/options.txt new file mode 100644 index 00000000..066dd783 --- /dev/null +++ b/doc/options.txt @@ -0,0 +1,60 @@ +-x An input file that has the node id followed by the position values. + +-a An input file that has the node id followed by the position values, denoting the anchor nodes. + +-m An input file that has the node id followed by the mass values. + +-t The number of threads to spawn. This is capped by 100. If not provided, the value 1 will be used. + +-i The maximum number of iterations. 250000 by default. + +-s The Special Spring Constant (the value of k in F=-kx for special interactions). 10 by default. + +-k The Casual Spring Constant (the value of k in F=-kx for casual interactions). 10 by default. + +-r The neighborhood radius for each particle. It defines the interaction range for casual (generally repulsive) interactions. 1 by default. + +-T The time step for each iteration. 0.001 by default. This is used as the time interval of force application on each iteration to determine the position adjustment of a particle. + +-S The 'radius' of each node. 0.01 by default. + +-M The 'mass' of each node. 1 by default. + +-R The radius of the outer perimeter. If not provided, the Nth root of the nodes count will be used, where N is the number of dimensions (either 2 or 3). + +-W The write interval. This is the number of iterations between consecutive stats dumps. 0 by default, meaning off. + +-z Id of the root node you want to use. If not provided, the root node will be attempted to be determined automatically. + +-o Path of the output file. If not provided, "lgl.out" will be used. + +-l Write out the edge level map, to a file having path .edge_levels . + Each time this option is provided, it toggles the prior value of this setting of whether to write out the edge level map or not. + The output indicates which 'level' each edge is on. The level is determined as the hop number from the root node. + +-e Output the mst used - the tree used to guide the layout, to a file having path .mst.lgl . + +-O Use original weights. If this option isn't provided, the weights will be generated from negative adjacent vertex count. + +-y Layout the tree only. + +-I Don't show layout progress, be quiet (kinda) + +-q The equilibrium distance. If two particles are closer to each other than this distance, then they will repulse each other. 0.5 by default. + +-E Ellipse factors. The format is one or more real numbers, separated from each other by the 'x' character. + Each number indicates the stretching factor to use for the corresponding dimension. If the count of the provided factors is less than the count of dimensions, + then the last provided factor will be used as the factor for subsequent dimensions too. + This is used to stretch the layout by the given factors, resulting in an ellipse-looking image rather than a circle-looking one. + +-u Placement distance, which is the distance you want the next level to be placed with respect to the previous level. + If this float value is not given, then a formula calculates the placement distance. + +-v Placement radius is a measure of the placement density. If not provided, the value 0.1 will be used. + +-L Place the leafs close by. This applies to trees more than graphs. + Setting this option will place the child vertices very near the parent vertex if all of its children have none themselves. + +-D Remove from processing the uninitialized-position nodes that are disconnected from the larger graph, + even if those nodes are connected to each other between themselves. + Currently only supported if the -x option is provided too, otherwise will have no effect. diff --git a/include/aPthread.hpp b/include/aPthread.hpp index 876e30c9..55b2a47f 100644 --- a/include/aPthread.hpp +++ b/include/aPthread.hpp @@ -231,12 +231,21 @@ class Amutex { public: Amutex( const pthread_mutex_t& m ) : mutex_(m) { } - Amutex( ) : mutex_() { } + Amutex() + : mutex_() + { + const int err = pthread_mutex_init( &mutex_, nullptr ); + if ( err != 0 ) + throw std::runtime_error( "Mutex initialization failed with error code " + std::to_string( err ) ); + } void copy( const Amutex& m ) { mutex_=m.mutex_; } int lock() { - return pthread_mutex_lock(&mutex_); + const int ret = pthread_mutex_lock(&mutex_); + if ( ret ) + std::cerr << "pthread_mutex_lock returned nonzero: " << ret << '\n'; + return ret; } int trylock() { @@ -250,9 +259,6 @@ class Amutex { Amutex& operator= ( const Amutex& m ) { Amutex::copy(m); return *this; } - - virtual ~Amutex() { } - }; //-------------------------------------------------- diff --git a/include/calcFuncs.h b/include/calcFuncs.h index 76116cd0..69f908d3 100644 --- a/include/calcFuncs.h +++ b/include/calcFuncs.h @@ -22,7 +22,7 @@ //------------------------------------------------ using namespace boost; -typedef adjacency_list< listS , vecS , directedS > out_graph; +typedef adjacency_list< vecS, vecS, directedS > out_graph; //------------------------------------------------ @@ -95,6 +95,12 @@ bool doesVertexHaveAnyChildren( Graph_t& G, Graph_t::vertex_descriptor v , out_g EllipseFactors parseEllipseFactors( const std::string& optionStr ); +// Attempts to initialize any particle positions that are not initialized yet, by way of interpolation from its neighbors which have initialized positions already, if any. +// This is done by picking the "center point" of those neighbors' positions to be used as the initial position for a particle that wasn't initialized before. +// The process continues until either all particles have initial positions properly set, or no further progress can be made (due to isolated and totally uninitialized islands). +// Also, the progress of the stages and the final accomplishment is printed to stdout. +void interpolateUninitializedPositions( PCChaperone& chaperone, const Graph_t::boost_graph& g, bool remove_disconnected_nodes ); + //------------------------------------------------ #endif diff --git a/include/cube.hpp b/include/cube.hpp index 805d9301..e4dc3faf 100644 --- a/include/cube.hpp +++ b/include/cube.hpp @@ -91,9 +91,6 @@ class Cube { bool operator!= ( const Cube_& c ) const { return !(*this==c); } - - virtual ~Cube(){ } - }; //------------------------------------------------------------ diff --git a/include/fixedVec.hpp b/include/fixedVec.hpp index 6a11c317..c0378481 100644 --- a/include/fixedVec.hpp +++ b/include/fixedVec.hpp @@ -25,23 +25,30 @@ #include #include +#include //---------------------------------------------------- -template < typename prec_ , unsigned int dimension_ > +template < typename T > +inline auto sqr( T t ) -> decltype( t * t ) +{ + return t * t; +} + +template < typename T, unsigned int dimension_ > class FixedVec { public: + using prec_ = decltype( std::declval< T & >() + std::declval< T & >() ); // this handles atomics as well as raw arithmetic types typedef prec_ precision; typedef unsigned int size_type; - typedef prec_ * iterator; - typedef const prec_ * const_iterator; + typedef T * iterator; + typedef const T * const_iterator; private: - typedef FixedVec Vec_; + typedef FixedVec< T, dimension_ > Vec_; - protected: - prec_ orig[dimension_]; // The origin + T orig[dimension_]; // The origin public: // CONSTRUCTORS @@ -57,9 +64,10 @@ class FixedVec { const_iterator end() const { return &orig[0]+dimension_; } // MUTATORS - void copy( const Vec_& p ) { + template < typename U > + void copy( const FixedVec< U, dimension_ > &p ) { for ( size_type ii=0; ii + Vec_ &operator=( const FixedVec< U, dimension_ > &p2 ) + { + Vec_::copy(p2); + return *this; } void operator-= ( const Vec_& p2 ) { @@ -182,7 +183,8 @@ class FixedVec { orig[ii] -= p2[ii]; } - void operator+= ( const Vec_& p2 ) { + template < typename U > + void operator+=( const FixedVec< U, dimension_ > &p2 ) { for ( size_type ii=0; ii -double euclideanDistance( Iterator begin1, Iterator end1, Iterator begin2 ) +double euclideanDistanceSquared( Iterator begin1, Iterator end1, Iterator begin2 ) { double d = 0; for ( ; begin1!=end1; ++begin1, ++begin2 ) { double dx = *begin2 - *begin1; d += dx * dx; } - return sqrt(d); + return d; +} + +template < typename Iterator > +double euclideanDistance( Iterator begin1, Iterator end1, Iterator begin2 ) +{ + return sqrt( euclideanDistanceSquared( begin1, end1, begin2 ) ); } //------------------------------------------- diff --git a/include/graph.hpp b/include/graph.hpp index 461d6e3b..ab2830e1 100644 --- a/include/graph.hpp +++ b/include/graph.hpp @@ -761,7 +761,7 @@ void readLGL( Graph& g , const char * file , typename Graph::weight_type cutoff typedef typename Graph::boost_graph BG; typedef typename Graph::vertex_index_map VIM; typedef boost::tokenizer< boost::char_separator > tokenizer; - typename Graph::weight_type w; + typename Graph::weight_type w{}; // value-initializing to avoid non-legit warning on line 804: ‘w’ may be used uninitialized in this function [-Wmaybe-uninitialized] std::ifstream in( file ); if ( !in ) { diff --git a/include/grid.hpp b/include/grid.hpp index 5768ca9d..dab6d0a9 100644 --- a/include/grid.hpp +++ b/include/grid.hpp @@ -201,8 +201,7 @@ class Grid : public Amutex { return *(voxels_+entry); } - virtual ~Grid(){ delete [] voxels_; } - + ~Grid() { delete [] voxels_; } }; namespace NbhrVoxelPositions { @@ -470,8 +469,7 @@ class GridIter { iterator::copy(g); return *this; } - virtual ~GridIter() { /* Don't delete any voxels */ } - + ~GridIter() { /* Don't delete any voxels */ } }; ///////////////////////////////////////////////////////////////////////////// @@ -494,7 +492,8 @@ void _place_particle( Particle& p , Grid& g ) { typename Grid::voxel_type * vox = g.getVoxelFromPosition( p.X() ); if ( vox == 0 ) { return _placement_error( p , g , "Entry Is Outside Of Grid" ); } - vox->lock(); + if ( vox->lock() != 0 ) + return _placement_error( p , g , "Failed to acquire lock on voxel" ); vox->insert( p ); vox->unlock(); p.container( vox->index() ); diff --git a/include/gridSchedual.hpp b/include/gridSchedual.hpp index a6596c02..2f5e72eb 100644 --- a/include/gridSchedual.hpp +++ b/include/gridSchedual.hpp @@ -129,8 +129,7 @@ class GridSchedual_MTS { } } - virtual ~GridSchedual_MTS() { delete [] voxelList; } - + ~GridSchedual_MTS() { delete [] voxelList; } }; //--------------------------------------------------------------------------- diff --git a/include/mutualMap.hpp b/include/mutualMap.hpp index 532fe567..b5031db4 100644 --- a/include/mutualMap.hpp +++ b/include/mutualMap.hpp @@ -44,7 +44,6 @@ class MutualMap template < typename t1 , typename t2 > t2 findEntryInMap( const std::map * m , const t1& t ) const - throw (std::invalid_argument) { typename std::map::const_iterator i = m->find( t ); if ( i == m->end() ) { throw std::invalid_argument("mutualMap Find Failed"); } @@ -102,12 +101,12 @@ class MutualMap return i != latter2former.end(); } - former findFormer( const latter& l ) const throw (std::invalid_argument) + former findFormer( const latter &l ) const { return findEntryInMap( &latter2former , l ); } - latter findLatter( const former& f ) const throw (std::invalid_argument) + latter findLatter( const former &f ) const { return findEntryInMap( &former2latter , f ); } diff --git a/include/particle.hpp b/include/particle.hpp index b42ec9a9..0856f25b 100644 --- a/include/particle.hpp +++ b/include/particle.hpp @@ -24,12 +24,14 @@ //---------------------------------------------------- #include -#include +#include #include "fixedVec.hpp" #include "aPthread.hpp" #include "fixedVecOperations.hpp" #include +#include + //---------------------------------------------------- template< typename prec_ , unsigned int dimension_ > @@ -43,9 +45,12 @@ class Particle { enum { dimension = dimension_ }; typedef prec_ precision; typedef FixedVec vec_type; + // using boost::atomic instead of std::atomic because the latter doesn't have op+= or fetch_add et al. for floating-point specializations until C++20 + // TODO C++20: std::atomic + typedef FixedVec< boost::atomic< prec_ >, dimension > atomic_vec_type; protected: - long container_; // This is the number of the container + long container_ = -1; // This is the number of the container // the particle resides in. prec_ radius_; // Particle is spherical/circular prec_ mass_; @@ -58,14 +63,14 @@ class Particle { public: vec_type x; // position in x,y,z //vec_type v; // velocity in x,y,z - vec_type f; // force in x,y,z + atomic_vec_type f; // force in x,y,z public: Particle( const Particle_& p){ Particle_::copy(p); } Particle(){ Particle_::resetValues(); } const vec_type& X() const { return x; } - const vec_type& F() const { return f; } + const atomic_vec_type &F() const { return f; } const std::string& id() const { return id_; } void id( const string& i ) { id_=i; } @@ -73,8 +78,13 @@ class Particle { bool isAnchor() const { return isAnchor_; } void markAnchor() { isAnchor_ = true; } + bool isPositionInitialized() const noexcept { + // TODO 'initialize' and compare against a big number instead of 0? + return !std::all_of( X().begin(), X().end(), [] ( float x ) { return x == 0.f; } ); + } + bool collisionCheck( const Particle_& vp ) const { - return ( x.distance(vp.x) <= (radius()+vp.radius()) ); + return x.distanceSquared( vp.x ) <= sqr( radius() + vp.radius() ); } // void incInteractionCtr( unsigned int i ) { interactionCtr_+=i; } @@ -162,8 +172,6 @@ class Particle { /* friend std::ostream& */ /* operator<< ( std::ostream& o , const Particle_& p ); */ - - virtual ~Particle(){} }; //---------------------------------------------------- diff --git a/include/particleContainer.hpp b/include/particleContainer.hpp index 34e8ef61..0ae85134 100644 --- a/include/particleContainer.hpp +++ b/include/particleContainer.hpp @@ -40,73 +40,64 @@ class ParticleContainer { enum { dimension = Particle::dimension }; typedef Particle particle_type; typedef typename particle_type::precision precision; - typedef unsigned int size_type; - typedef Particle* iterator; + using container_type = std::vector< Particle >; + using size_type = typename container_type::size_type; + using iterator = typename container_type::iterator; protected: - size_type size_; - Particle * particles; - precision avg_temp; - precision avg_dx; + container_type particles_; + precision avg_temp = 0; + precision avg_dx = 0; public: vector ids; // This will hold the names void initParticles() { - for ( size_type ii=0; ii #include #include +#include #include // for strdup #include "particle.hpp" @@ -62,7 +63,7 @@ class ParticleContainerChaperone { vec_type initVel_; vec_type vel_; vec_type initPos_; - vec_type pos_; + std::unordered_map< std::string, vec_type > positions_from_file_; precision radius_; precision initMass_; char * file_in[3]; // X V M @@ -140,7 +141,16 @@ class ParticleContainerChaperone { } #endif - bool setXFromFile( Particle& p , const string& id2check= "" ) { + bool setXFromFile( Particle &p , const string &id2check ) { +#if 1 + // assuming id2check isn't empty for simplicity of this optimized implementation + assert( !id2check.empty() ); + const auto it = positions_from_file_.find( id2check ); + if ( it != positions_from_file_.end() ) { + p.X( it->second ); + return true; + } +#else string id = ""; if ( !streams_in[_X_FILE__].eof() && streams_in[_X_FILE__] >> id ) { if ( id2check != "" && id2check != id ) { @@ -150,22 +160,26 @@ class ParticleContainerChaperone { streams_in[_X_FILE__] >> pos_[jj]; } pc_[ii].X(pos_); - return 1; + return true; } } - PCC_::orderingError(file_in[_X_FILE__]); + // Warn that `id` found in initial coordinates file is not found in the main input file? That could be too noisy in some use cases... + float ignored; + for ( size_type ii=0; ii> ignored; + return false; } for ( size_type ii=0; ii> pos_[ii]; } p.X(pos_); - return 1; - } else { - return 0; + return true; } +#endif + return false; } - bool setXFromAnchors( Particle& p, const std::string& id2check ) + bool setXFromAnchors( Particle& p, const std::string& id2check ) const { typename AnchorPositions_t::const_iterator const it = anchorPositions_.find( id2check ); if ( it == anchorPositions_.end() ) @@ -215,7 +229,7 @@ class ParticleContainerChaperone { for ( unsigned int jj=0; jj<3; ++jj ) { file_in[jj]=0; file_out[jj]=0; file_out_flag[jj]=0; } - vel_=0; initVel_=0; initPos_=0; pos_=0; radius_=0; + vel_=0; initVel_=0; initPos_=0; radius_=0; initMass_=0; posRange_=0; velRange_=0; randomPos_=0; randomVel_=0; } @@ -263,10 +277,10 @@ class ParticleContainerChaperone { precision initMass() { return initMass_; } precision initRadius() { return radius_; } - virtual ~ParticleContainerChaperone() { + ~ParticleContainerChaperone() { for ( int i=0; i<3; ++i ) { - if ( file_in[i] ) { delete file_in[i]; } - if ( file_out[i] ) { delete file_out[i]; } + delete file_in[i]; + delete file_out[i]; } } @@ -288,6 +302,21 @@ class ParticleContainerChaperone { throw std::domain_error( "Anchor file input failed around node '" + id + '\'' ); } + void readXin() + { + auto &is = streams_in[_X_FILE__]; + std::string id; + vec_type pos; + while ( is >> id ) { + if ( !readPos( is, pos ) ) + throw std::domain_error( "Initial position input failed for node '" + id + '\'' ); + if ( !positions_from_file_.insert( { std::move( id ), pos } ).second ) + throw std::domain_error( "Node '" + id + "' has already been specified earlier in the positions input file!" ); + } + if ( !is.eof() ) + throw std::domain_error( "Initial positions file input failed around node '" + id + '\'' ); + } + static std::istream &readPos( std::istream &is, vec_type &pos ) { for ( size_type ii = 0; ii < dimension; ++ii ) @@ -302,9 +331,10 @@ template < typename Particle > void ParticleContainerChaperone::initAllParticles() { PCC_::openInFiles(); + readXin(); size_type nodeCount = pc_.size(); for ( size_type ii=0; ii -#include - -//--------------------------------------------- - -template < typename Particle > -class ParticleInteractionHandler { - -private: - typedef ParticleInteractionHandler< Particle > PIH_; - -public: - enum { dimension = Particle::dimension }; - typedef Particle particle_type; - typedef typename particle_type::precision precision; - typedef typename particle_type::vec_type vec_type; - - protected: - precision timeStep_; - precision springConstant_; - precision eqDistance_; - precision forceConstraint_; - precision noiseAmplitude_; - int id_; - - // This encourages pass throughs by giving a boost to whatever - // direction particles were going in the first place - void handleCollision( Particle& p1, Particle& p2 ) const { - const vec_type& f1 = p1.F(); - const vec_type& f2 = p2.F(); - const precision mag1 = p1.F().magnitude(); - precision mag1m1 = 0; - if ( mag1 > .01 ) { mag1m1 = 1.0/mag1; } - else { mag1m1=.1; } - const precision mag2 = p2.F().magnitude(); - precision mag2m1 = 0; - if ( mag2 > .01 ) { mag2m1 = 1.0/mag2; } - else { mag2m1=.1; } - - vec_type f1_; - vec_type f2_; - - for ( unsigned int ii=0; ii forceConstraint_ ) { - p1.f[d] = forceConstraint_; - } else if ( p1.f[d] < -1.0 * forceConstraint_ ) { - p1.f[d] = -1.0*forceConstraint_; - } - } - p1.unlock(); - // p1.print(cout); - } - - void noiseAmplitude( precision n ) { noiseAmplitude_=n; } - precision noiseAmplitude() const { return noiseAmplitude_; } - - void addNoise( Particle& p1 ) const { - p1.lock(); - precision factor = 1.0; - for ( unsigned d=0; d eqDistance_ ) dx = 1.0 / ( dx * dx ); - precision f = dx * scale; - f_[ii] = f; - fm1_[ii] = -1.0*f; - } - -// cout << "d: " << magx1x2 << "\tsep: " << sepFromIdeal << "\tSc: " -// << scale << "\tEQ: " << eqDistance_ << endl; -// cout << "B4" << endl; -// p1.print(); -// p2.print(); - - p1.lock(); - p1.add2F(f_); - p1.unlock(); - - p2.lock(); - p2.add2F(fm1_); - p2.unlock(); - -// cout << "AFTER" << endl; -// p1.print(); -// p2.print(); - - } - - void integrate( Particle& p1 ) const { - PIH_::integrateFirstOrder(p1); - } - - void integrate( Particle& p1 , precision t ) const { - PIH_::integrateFirstOrder(p1,t); - } - - void integrateFirstOrder( Particle& p1 , precision t ) const { - for (unsigned int ii=0; ii((precision).05,abs(finc)); } - else { finc = min((precision).05,finc); } - p1.x[ii] += finc; - } - } - - void integrateFirstOrder( Particle& p1 ) const { - PIH_::integrateFirstOrder(p1,timeStep_); - } - - void print( std::ostream& o=std::cout ) const { - o << "PIH:" << id_ << "\tT: " << timeStep_ - << "\tk:" << springConstant_ << "\ta: " << eqDistance_ - << '\n'; - } - - PIH_& operator= ( const PIH_& p ) { - PIH_::copy(p); return *this; - } - - bool operator== ( const PIH_& p ) const { - return id_==p.id_; - } - - virtual ~ParticleInteractionHandler() { } - -}; - -//--------------------------------------------- - -#endif diff --git a/include/particleStats.hpp b/include/particleStats.hpp index 9d80d8a5..6f30655e 100644 --- a/include/particleStats.hpp +++ b/include/particleStats.hpp @@ -137,9 +137,6 @@ class ParticleStats { << "CollectStatsAt: " << iteration2CollectStats << '\t'; ParticleStats_::printStats(o); } - - virtual ~ParticleStats() { } - }; diff --git a/include/sphere.hpp b/include/sphere.hpp index 4910dcb0..53fb404e 100644 --- a/include/sphere.hpp +++ b/include/sphere.hpp @@ -148,12 +148,10 @@ inline void randomPointOnSurface( const Sphere& s , typename Sphere::vec_type& r ) { #if 1 - typedef typename Sphere::vec_type vec_type; uniform_on_sphere_vec( r , s.dimension() ); #else // TODO use this instead of the hand-made code? typedef typename boost::hellekalek1995 precision; - typedef typename Sphere::vec_type vec_type; precision rr(++randomPointOnSurfaceCall___); boost::uniform_on_sphere<> usph( s.dimension() ); std::vector v = usph( rr ); diff --git a/include/thread_pool.hpp b/include/thread_pool.hpp new file mode 100644 index 00000000..9b640dbf --- /dev/null +++ b/include/thread_pool.hpp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2020 Gevorg Voskanyan, All Rights Reserved. +// +// This program 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 2 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, +// MA 02111-1307 USA +// +//-------------------------------------------------- +// A simple thread pool implementation, requiring C++11 +//-------------------------------------------------- + +#ifndef THREAD_POOL_HPP_INCLUDED +#define THREAD_POOL_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class thread_pool { +public: + explicit thread_pool( unsigned num_threads ) + : threads_( num_threads ) + { + for ( auto &t : threads_ ) + t = std::thread( &thread_pool::thread_function, this ); + } + + // thread_pool is neither copyable nor movable + + ~thread_pool() + { + { + std::lock_guard< std::mutex > lock( mutex_ ); + stop_ = true; + // if there are still tasks in task_queue_, they will get destroyed without having the chance to be executed, + // resulting in broken_promise exception being stored into them, which is fine + } + condvar_.notify_all(); + for ( auto &t : threads_ ) + t.join(); + } + + template < typename F, typename ... Args > + std::future< void > run( F&& f, Args&&... args ) + { + std::packaged_task< void () > task( std::bind( std::forward< F >( f ), std::forward< Args >( args )... ) ); + auto fut = task.get_future(); + { + std::lock_guard< std::mutex > lock( mutex_ ); + task_queue_.push( std::move( task ) ); + } + condvar_.notify_one(); + return fut; + } + +private: + std::vector< std::thread > threads_; + std::mutex mutex_; + std::condition_variable condvar_; + // the data members below are protected by mutex_ + std::queue< std::packaged_task< void () > > task_queue_; + bool stop_ = false; + + void thread_function() + { + while ( true ) { + std::packaged_task< void () > task; + { + std::unique_lock< std::mutex > lock( mutex_ ); + condvar_.wait( lock, [this] { return stop_ || !task_queue_.empty(); } ); + if ( stop_ ) + return; + assert( !task_queue_.empty() ); + task = std::move( task_queue_.front() ); + task_queue_.pop(); + } + task(); // running the task, outside of the lock + } + } +}; + +#endif // THREAD_POOL_HPP_INCLUDED diff --git a/include/timeKeeper.hpp b/include/timeKeeper.hpp index e024f495..0255af2a 100644 --- a/include/timeKeeper.hpp +++ b/include/timeKeeper.hpp @@ -99,8 +99,6 @@ class TimeKeeper { void operator+= ( prec_ dt ) { TK_::increment(dt); } - - virtual ~TimeKeeper() { } }; //---------------------------------------------------- diff --git a/include/voxel.hpp b/include/voxel.hpp index bf3692cb..60b840aa 100644 --- a/include/voxel.hpp +++ b/include/voxel.hpp @@ -22,7 +22,7 @@ #define _VOXEL_HPP_ #include -#include +#include #include "cube.hpp" #include "aPthread.hpp" @@ -38,7 +38,7 @@ class Voxel : public Cube< typename Occupant::precision , { private: - typedef typename std::set< Occupant * > OL_; + typedef typename std::unordered_set< Occupant * > OL_; typedef Voxel< Occupant > Voxel_; typedef Cube< typename Occupant::precision , Occupant::dimension > Cube_; @@ -117,9 +117,6 @@ class Voxel : public Cube< typename Occupant::precision , const Voxel_& operator= ( const Voxel_& v ) { Voxel_::copy(v); return *this; } - - virtual ~Voxel(){ } - }; //------------------------------------------------------------ diff --git a/lglview b/lglview new file mode 100755 index 00000000..f7e31921 --- /dev/null +++ b/lglview @@ -0,0 +1,2 @@ +#!/bin/sh +java -Xmx10G -Xms10G -cp ./Java/jar/LGLLib.jar Viewer2D.Viewer2D diff --git a/resources/bgpdata/oix-full-1997-11-23-1040.dat.bz2 b/resources/bgpdata/oix-full-1997-11-23-1040.dat.bz2 deleted file mode 100644 index b14f2b38..00000000 Binary files a/resources/bgpdata/oix-full-1997-11-23-1040.dat.bz2 and /dev/null differ diff --git a/resources/bgpdata/oix-full-1997-11-30-1040.dat.bz2 b/resources/bgpdata/oix-full-1997-11-30-1040.dat.bz2 deleted file mode 100644 index be4e3fd4..00000000 Binary files a/resources/bgpdata/oix-full-1997-11-30-1040.dat.bz2 and /dev/null differ diff --git a/resources/bgpdata/oix-full-1998-09-01-0940.dat.bz2 b/resources/bgpdata/oix-full-1998-09-01-0940.dat.bz2 deleted file mode 100644 index a35742e5..00000000 Binary files a/resources/bgpdata/oix-full-1998-09-01-0940.dat.bz2 and /dev/null differ diff --git a/resources/bgpdata/oix-full-1998-09-02-0940.dat.bz2 b/resources/bgpdata/oix-full-1998-09-02-0940.dat.bz2 deleted file mode 100644 index 2bd8849f..00000000 Binary files a/resources/bgpdata/oix-full-1998-09-02-0940.dat.bz2 and /dev/null differ diff --git a/resources/bgpdata/oix-full-1999-09-01-0940.dat.bz2 b/resources/bgpdata/oix-full-1999-09-01-0940.dat.bz2 deleted file mode 100644 index 3a2eb0fa..00000000 Binary files a/resources/bgpdata/oix-full-1999-09-01-0940.dat.bz2 and /dev/null differ diff --git a/resources/bgpdata/oix-full-1999-09-02-0940.dat.bz2 b/resources/bgpdata/oix-full-1999-09-02-0940.dat.bz2 deleted file mode 100644 index 655b88c2..00000000 Binary files a/resources/bgpdata/oix-full-1999-09-02-0940.dat.bz2 and /dev/null differ diff --git a/resources/images/internet_2001.png b/resources/images/internet_2001.png deleted file mode 100644 index 9968b415..00000000 Binary files a/resources/images/internet_2001.png and /dev/null differ diff --git a/resources/images/internet_2001_darkgreenblue.png b/resources/images/internet_2001_darkgreenblue.png deleted file mode 100644 index 3dcee877..00000000 Binary files a/resources/images/internet_2001_darkgreenblue.png and /dev/null differ diff --git a/resources/images/internet_2016.png b/resources/images/internet_2016.png deleted file mode 100644 index 32245fd0..00000000 Binary files a/resources/images/internet_2016.png and /dev/null differ diff --git a/resources/images/internet_2017.png b/resources/images/internet_2017.png deleted file mode 100644 index b79c15b0..00000000 Binary files a/resources/images/internet_2017.png and /dev/null differ diff --git a/resources/images/internet_2018.png b/resources/images/internet_2018.png deleted file mode 100644 index 6389faa8..00000000 Binary files a/resources/images/internet_2018.png and /dev/null differ diff --git a/resources/images/internet_2019.png b/resources/images/internet_2019.png deleted file mode 100644 index 4a47196e..00000000 Binary files a/resources/images/internet_2019.png and /dev/null differ diff --git a/resources/images/internet_blue_green.png b/resources/images/internet_blue_green.png deleted file mode 100755 index e1a252ef..00000000 Binary files a/resources/images/internet_blue_green.png and /dev/null differ diff --git a/resources/images/latest/bview_2000_09_transparent.png b/resources/images/latest/bview_2000_09_transparent.png deleted file mode 100644 index c0c325aa..00000000 Binary files a/resources/images/latest/bview_2000_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2001_09_transparent.png b/resources/images/latest/bview_2001_09_transparent.png deleted file mode 100644 index 29a69f19..00000000 Binary files a/resources/images/latest/bview_2001_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2002_09_transparent.png b/resources/images/latest/bview_2002_09_transparent.png deleted file mode 100644 index b847b8ca..00000000 Binary files a/resources/images/latest/bview_2002_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2003_09_transparent.png b/resources/images/latest/bview_2003_09_transparent.png deleted file mode 100644 index c1765ed5..00000000 Binary files a/resources/images/latest/bview_2003_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2004_09_transparent.png b/resources/images/latest/bview_2004_09_transparent.png deleted file mode 100644 index 5b97ab61..00000000 Binary files a/resources/images/latest/bview_2004_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2005_09_transparent.png b/resources/images/latest/bview_2005_09_transparent.png deleted file mode 100644 index 43131c17..00000000 Binary files a/resources/images/latest/bview_2005_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2006_09_transparent.png b/resources/images/latest/bview_2006_09_transparent.png deleted file mode 100644 index 6a9a2930..00000000 Binary files a/resources/images/latest/bview_2006_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2007_09_transparent.png b/resources/images/latest/bview_2007_09_transparent.png deleted file mode 100644 index 1241266d..00000000 Binary files a/resources/images/latest/bview_2007_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2008_09_transparent.png b/resources/images/latest/bview_2008_09_transparent.png deleted file mode 100644 index 0937d80c..00000000 Binary files a/resources/images/latest/bview_2008_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2009_09_transparent.png b/resources/images/latest/bview_2009_09_transparent.png deleted file mode 100644 index d670a6f3..00000000 Binary files a/resources/images/latest/bview_2009_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2010_09_transparent.png b/resources/images/latest/bview_2010_09_transparent.png deleted file mode 100644 index 84efd0d4..00000000 Binary files a/resources/images/latest/bview_2010_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2011_09_transparent.png b/resources/images/latest/bview_2011_09_transparent.png deleted file mode 100644 index e7e765ff..00000000 Binary files a/resources/images/latest/bview_2011_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2012_09_transparent.png b/resources/images/latest/bview_2012_09_transparent.png deleted file mode 100644 index 7528f89d..00000000 Binary files a/resources/images/latest/bview_2012_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2013_09_transparent.png b/resources/images/latest/bview_2013_09_transparent.png deleted file mode 100644 index f19cc335..00000000 Binary files a/resources/images/latest/bview_2013_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2014_09_transparent.png b/resources/images/latest/bview_2014_09_transparent.png deleted file mode 100644 index a801699b..00000000 Binary files a/resources/images/latest/bview_2014_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2015_09_transparent.png b/resources/images/latest/bview_2015_09_transparent.png deleted file mode 100644 index f538398c..00000000 Binary files a/resources/images/latest/bview_2015_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2016_09_transparent.png b/resources/images/latest/bview_2016_09_transparent.png deleted file mode 100644 index f60ff18f..00000000 Binary files a/resources/images/latest/bview_2016_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2017_09_transparent.png b/resources/images/latest/bview_2017_09_transparent.png deleted file mode 100644 index 079865da..00000000 Binary files a/resources/images/latest/bview_2017_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2018_09_transparent.png b/resources/images/latest/bview_2018_09_transparent.png deleted file mode 100644 index 254b920b..00000000 Binary files a/resources/images/latest/bview_2018_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2019_09_transparent.png b/resources/images/latest/bview_2019_09_transparent.png deleted file mode 100644 index e34b92dc..00000000 Binary files a/resources/images/latest/bview_2019_09_transparent.png and /dev/null differ diff --git a/resources/images/latest/bview_2020_09_transparent.png b/resources/images/latest/bview_2020_09_transparent.png deleted file mode 100644 index aeaa13ce..00000000 Binary files a/resources/images/latest/bview_2020_09_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20000901.0610.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20000901.0610.latest_dark.png deleted file mode 100644 index d79b873e..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20000901.0610.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20010901.1508.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20010901.1508.latest_dark.png deleted file mode 100644 index f707369c..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20010901.1508.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20020903.0454.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20020903.0454.latest_dark.png deleted file mode 100644 index 090df456..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20020903.0454.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20030903.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20030903.0000.latest_dark.png deleted file mode 100644 index 494f5fa3..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20030903.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20040902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20040902.0000.latest_dark.png deleted file mode 100644 index efe9f672..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20040902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20050902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20050902.0000.latest_dark.png deleted file mode 100644 index 03882a23..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20050902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20060902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20060902.0000.latest_dark.png deleted file mode 100644 index 1a9079bd..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20060902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20070902.0759.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20070902.0759.latest_dark.png deleted file mode 100644 index b62e5ed0..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20070902.0759.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20080902.0759.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20080902.0759.latest_dark.png deleted file mode 100644 index e3d634df..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20080902.0759.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20090902.0759.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20090902.0759.latest_dark.png deleted file mode 100644 index b9592269..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20090902.0759.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20100902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20100902.0000.latest_dark.png deleted file mode 100644 index 516b0a80..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20100902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20110902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20110902.0000.latest_dark.png deleted file mode 100644 index ad6b2406..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20110902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20120902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20120902.0000.latest_dark.png deleted file mode 100644 index 94941dc7..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20120902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20130902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20130902.0000.latest_dark.png deleted file mode 100644 index 8b75982c..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20130902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20140902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20140902.0000.latest_dark.png deleted file mode 100644 index 280a0500..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20140902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20150902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20150902.0000.latest_dark.png deleted file mode 100644 index da716e67..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20150902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20160902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20160902.0000.latest_dark.png deleted file mode 100644 index 05c24d12..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20160902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20170902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20170902.0000.latest_dark.png deleted file mode 100644 index 25bda42a..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20170902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20180902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20180902.0000.latest_dark.png deleted file mode 100644 index 17168afa..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20180902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/dark/bview.20190902.0000.latest_dark.png b/resources/images/latest_rrc00/dark/bview.20190902.0000.latest_dark.png deleted file mode 100644 index 5d720041..00000000 Binary files a/resources/images/latest_rrc00/dark/bview.20190902.0000.latest_dark.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20000901.0610.latest_light.png b/resources/images/latest_rrc00/light/bview.20000901.0610.latest_light.png deleted file mode 100644 index dee60c8f..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20000901.0610.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20010901.1508.latest_light.png b/resources/images/latest_rrc00/light/bview.20010901.1508.latest_light.png deleted file mode 100644 index 3db1c7f0..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20010901.1508.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20020903.0454.latest_light.png b/resources/images/latest_rrc00/light/bview.20020903.0454.latest_light.png deleted file mode 100644 index 7c21873c..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20020903.0454.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20030903.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20030903.0000.latest_light.png deleted file mode 100644 index 3431e6c5..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20030903.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20040902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20040902.0000.latest_light.png deleted file mode 100644 index 454d6916..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20040902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20050902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20050902.0000.latest_light.png deleted file mode 100644 index 7938f6df..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20050902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20060902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20060902.0000.latest_light.png deleted file mode 100644 index 71e8a0ea..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20060902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20070902.0759.latest_light.png b/resources/images/latest_rrc00/light/bview.20070902.0759.latest_light.png deleted file mode 100644 index 228790df..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20070902.0759.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20080902.0759.latest_light.png b/resources/images/latest_rrc00/light/bview.20080902.0759.latest_light.png deleted file mode 100644 index 994fa571..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20080902.0759.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20090902.0759.latest_light.png b/resources/images/latest_rrc00/light/bview.20090902.0759.latest_light.png deleted file mode 100644 index 319abe28..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20090902.0759.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20100902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20100902.0000.latest_light.png deleted file mode 100644 index 908aa431..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20100902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20110902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20110902.0000.latest_light.png deleted file mode 100644 index 95c7c361..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20110902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20120902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20120902.0000.latest_light.png deleted file mode 100644 index 6f3e49a2..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20120902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20130902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20130902.0000.latest_light.png deleted file mode 100644 index 15461521..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20130902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20140902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20140902.0000.latest_light.png deleted file mode 100644 index 55731106..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20140902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20150902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20150902.0000.latest_light.png deleted file mode 100644 index 1cc23d44..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20150902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20160902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20160902.0000.latest_light.png deleted file mode 100644 index 9ede37b2..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20160902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20170902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20170902.0000.latest_light.png deleted file mode 100644 index 99c84c63..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20170902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20180902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20180902.0000.latest_light.png deleted file mode 100644 index 454ddb43..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20180902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/light/bview.20190902.0000.latest_light.png b/resources/images/latest_rrc00/light/bview.20190902.0000.latest_light.png deleted file mode 100644 index 4eee2ace..00000000 Binary files a/resources/images/latest_rrc00/light/bview.20190902.0000.latest_light.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20000901.0610.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20000901.0610.latest_transparent.png deleted file mode 100644 index f3871f81..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20000901.0610.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20010901.1508.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20010901.1508.latest_transparent.png deleted file mode 100644 index 44248332..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20010901.1508.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20020903.0454.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20020903.0454.latest_transparent.png deleted file mode 100644 index 0a39732d..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20020903.0454.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20030903.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20030903.0000.latest_transparent.png deleted file mode 100644 index 51b2bf5e..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20030903.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20040902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20040902.0000.latest_transparent.png deleted file mode 100644 index 7cab61b9..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20040902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20050902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20050902.0000.latest_transparent.png deleted file mode 100644 index ca4a8368..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20050902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20060902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20060902.0000.latest_transparent.png deleted file mode 100644 index 4f430a12..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20060902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20070902.0759.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20070902.0759.latest_transparent.png deleted file mode 100644 index d69bd904..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20070902.0759.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20080902.0759.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20080902.0759.latest_transparent.png deleted file mode 100644 index 30a51d88..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20080902.0759.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20090902.0759.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20090902.0759.latest_transparent.png deleted file mode 100644 index 7ca7526e..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20090902.0759.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20100902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20100902.0000.latest_transparent.png deleted file mode 100644 index 3c5bbb7e..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20100902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20110902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20110902.0000.latest_transparent.png deleted file mode 100644 index dca51501..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20110902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20120902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20120902.0000.latest_transparent.png deleted file mode 100644 index 0ced7a62..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20120902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20130902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20130902.0000.latest_transparent.png deleted file mode 100644 index 9eed518d..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20130902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20140902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20140902.0000.latest_transparent.png deleted file mode 100644 index 23d74e10..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20140902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20150902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20150902.0000.latest_transparent.png deleted file mode 100644 index 8b487ca3..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20150902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20160902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20160902.0000.latest_transparent.png deleted file mode 100644 index 1ae55ba0..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20160902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20170902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20170902.0000.latest_transparent.png deleted file mode 100644 index 5ae9d89a..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20170902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20180902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20180902.0000.latest_transparent.png deleted file mode 100644 index 3292deaf..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20180902.0000.latest_transparent.png and /dev/null differ diff --git a/resources/images/latest_rrc00/transparent/bview.20190902.0000.latest_transparent.png b/resources/images/latest_rrc00/transparent/bview.20190902.0000.latest_transparent.png deleted file mode 100644 index 4b4b12f6..00000000 Binary files a/resources/images/latest_rrc00/transparent/bview.20190902.0000.latest_transparent.png and /dev/null differ diff --git a/src/Makefile b/src/Makefile index f2fc4897..78e49841 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ CPP=c++ -CPP_FLAGS=-std=c++11 -Wall -O3 -ftemplate-depth=100 -g +CPP_FLAGS=-std=c++11 -Wall -O3 -ftemplate-depth=100 -g -DBOOST_ALLOW_DEPRECATED_HEADERS SOURCES= lglayout.C calcFuncs.C OBJECTS= lglayout.o calcFuncs.o @@ -35,4 +35,4 @@ clean: tidy: rm -f *.o core lglayout lglbreakup lglrebuild lglfileconvert -.PHONY: all clean tidy \ No newline at end of file +.PHONY: all clean tidy diff --git a/src/calcFuncs.C b/src/calcFuncs.C index a48bdffb..83c26f97 100644 --- a/src/calcFuncs.C +++ b/src/calcFuncs.C @@ -23,6 +23,7 @@ #include #include +#include "thread_pool.hpp" #include "particleInteractionHandler.hpp" #include "calcFuncs.h" @@ -61,7 +62,6 @@ void * calcInteractions ( void * arg_ ) void * integrateParticles ( void * arg_ ) { typedef Graph_t::vertex_iterator Vi; - typedef Graph_t::out_edge_iterator Ei; //cout << "integrateParticles" << endl; ThreadArgs& args = *(static_cast(arg_)); long whichThread = args.whichThread; @@ -183,6 +183,16 @@ void beginSimulation( ThreadContainer& threads , long threadCount = threads.size(); unsigned int currentLevel = 1; + thread_pool threadpool( threadCount ); + std::vector< std::future< void > > futures; + futures.reserve( threadCount ); + + const auto wait_for_futures = [&futures] { + for ( auto &f : futures ) + f.get(); + futures.clear(); + }; + while( currentLevel <= totalLevels ) { // Place and initialize the next layer of the graph @@ -199,7 +209,7 @@ void beginSimulation( ThreadContainer& threads , prec_t avgPrevious = 0.0; prec_t dx = 10000000.; int iterationCtr = 0; - + do { // Repulsive terms @@ -207,29 +217,33 @@ void beginSimulation( ThreadContainer& threads , threadArgs[ii].currentLevel = currentLevel; threadArgs[ii].nodeHandler->springConstant( threadArgs[ii].casualSpringConstant ); threadArgs[ii].nodeHandler->eqDistance( threadArgs[ii].nbhdRadius ); - threads[ii].create( calcInteractions , - static_cast(&threadArgs[ii]) ); - } threads.wait(); + futures.push_back( threadpool.run( calcInteractions , + static_cast(&threadArgs[ii]) ) ); + } + wait_for_futures(); // Attractive terms for ( long ii=0; iispringConstant( threadArgs[ii].specialSpringConstant ); threadArgs[ii].nodeHandler->eqDistance( threadArgs[ii].eqDistance ); - threads[ii].create( onlyEdgeInteractions , - static_cast(&threadArgs[ii]) ); - } threads.wait(); + futures.push_back( threadpool.run( onlyEdgeInteractions , + static_cast(&threadArgs[ii]) ) ); + } + wait_for_futures(); // Integrate for next time step for ( long ii=0; ii(&threadArgs[ii]) ); - } threads.wait(); + futures.push_back( threadpool.run( integrateParticles , + static_cast(&threadArgs[ii]) ) ); + } + wait_for_futures(); // Collect stats for progress for ( long ii=0; ii(&threadArgs[ii]) ); - } threads.wait(); + futures.push_back( threadpool.run( collectEdgeStats , + static_cast(&threadArgs[ii]) ) ); + } + wait_for_futures(); prec_t dxNew = collectOutput( &threadArgs[0] , chaperone ); if ( ! silentOutput ) { @@ -566,3 +580,69 @@ EllipseFactors parseEllipseFactors( const std::string& optionStr ) } //---------------------------------------------------------- + +void interpolateUninitializedPositions( PCChaperone& chaperone, const Graph_t::boost_graph& g, bool remove_disconnected_nodes ) +{ + std::size_t num_uninitialized_positions_still, num_uninitialized_positions_before; +#if 0 + std::ofstream interpolation_log( "interpolation.log" ); +#endif + do { + num_uninitialized_positions_before = num_uninitialized_positions_still = 0; + for ( std::size_t ii = 0; ii < chaperone.pc_.size(); ++ii ) { + if ( !chaperone.pc_[ii].isPositionInitialized() ) { + ++num_uninitialized_positions_before; + std::size_t count_initialized_neighbors = 0; + Graph_t::out_edge_iterator eb, ee; + for ( tie( eb, ee ) = out_edges( ii, g ); eb != ee; ++eb ) { + const auto src = source( *eb, g ), tgt = target( *eb, g ); + assert( src == ii || tgt == ii ); + const auto other = src == ii ? tgt : src; + if ( chaperone.pc_[other].isPositionInitialized() ) { + ++count_initialized_neighbors; + for ( std::size_t dim = 0; dim < chaperone.pc_[ii].X().size(); ++dim ) + chaperone.pc_[ii].x[dim] += chaperone.pc_[other].X()[dim]; + } + } + if ( count_initialized_neighbors ) + for ( float &coord : chaperone.pc_[ii].x ) + coord /= count_initialized_neighbors; + else + ++num_uninitialized_positions_still; +#if 0 + interpolation_log << chaperone.pc_[ii].id() << " was uninitialized, now coords are"; + for ( float coord : chaperone.pc_[ii].X() ) + interpolation_log << ' ' << coord; + interpolation_log << '\n'; +#endif + } +#if 0 + else { + interpolation_log << chaperone.pc_[ii].id() << " was already initialized with coords"; + for ( float coord : chaperone.pc_[ii].X() ) + interpolation_log << ' ' << coord; + interpolation_log << '\n'; + } +#endif + } + cout << "\nOut of " << num_uninitialized_positions_before << " uninitialized positions that had remained, " + << num_uninitialized_positions_before - num_uninitialized_positions_still << " have just been interpolated"; + // until either finished or there is no more progress being made + } while ( num_uninitialized_positions_still > 0 && num_uninitialized_positions_still < num_uninitialized_positions_before ); + + if ( num_uninitialized_positions_still ) { + cout << "\nThere are " << num_uninitialized_positions_still << " nodes that are DISCONNECTED from any nodes which had their positions initialized!\nTHOSE NODES ARE:\n"; + for ( std::size_t ii = 0; ii < chaperone.pc_.size(); ++ii ) + if ( !chaperone.pc_[ii].isPositionInitialized() ) + cout << '\t' << chaperone.pc_[ii].id() << '\n'; + if ( remove_disconnected_nodes ) { + cout << "Removing them from the graph before further processing...\n"; + for ( std::size_t ii = chaperone.pc_.size(); ii > 0; --ii ) + if ( !chaperone.pc_[ii - 1].isPositionInitialized() ) + chaperone.pc_.erase( ii - 1 ); + } + cout << std::endl; + } + else + cout << "\nInterpolation of uninitialized positions completed successfully." << std::endl; +} diff --git a/src/lglayout.C b/src/lglayout.C index 5a69c707..740e71a0 100644 --- a/src/lglayout.C +++ b/src/lglayout.C @@ -83,11 +83,12 @@ try bool doesWriteLogFile = true; bool placeLeafsClose = false; bool isSilent = false; // Show progress + bool disregardDisconnectedNodes = false; timer.max(MAXITER); timer.timeStep(PART_TIME_STEP); - while ( (optch = getopt(argc,argv,"x:a:t:m:M:i:s:r:k:T:R:S:W:z:o:leOyu:v:Iq:E:L")) != -1 ) + while ( (optch = getopt(argc,argv,"x:a:t:m:M:i:s:r:k:T:R:S:W:z:o:leOyu:v:Iq:E:L:D")) != -1 ) { switch (optch) { @@ -116,6 +117,7 @@ try case 'q': eqDistance = atof(optarg); break; case 'E': ellipseFactors = parseEllipseFactors(optarg); break; case 'L': placeLeafsClose = true; break; + case 'D': disregardDisconnectedNodes = true; break; default : cerr << "Bad option -\t" << (char) optch << '\n'; exit(EXIT_FAILURE); } @@ -164,6 +166,8 @@ try chaperone.initRadius(nodeSizeRadius); chaperone.posOutFile( outfile ); chaperone.initAllParticles(); + if ( initPosFile ) + interpolateUninitializedPositions( chaperone, G.boostGraph(), disregardDisconnectedNodes ); // without this call the_internet's results become unacceptably stretched and ugly cout << "Done." << endl; cout << "Initializing grid and placing particles..." << flush; diff --git a/src/lglrebuild.C b/src/lglrebuild.C index 8b6300de..a38687da 100644 --- a/src/lglrebuild.C +++ b/src/lglrebuild.C @@ -257,7 +257,7 @@ void writeResults( const Mol& m, const EllipseFactors& ellipseFactors, const cha bool checkMoleculeAgainstTable( const Mol& m , EDTable& table ) { typedef Mol::size_type size_type; - typedef EDTable::ResultList EL; + //typedef EDTable::ResultList EL; for ( size_type ii=0; ii