package Utils; import AngioTool.NumberEntry; import AngioTool.SizeColorEntry; import AngioTool.SimpleFileFilter; import Xlsx.*; import java.awt.Dimension; import java.awt.Font; import java.lang.reflect.Field; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.ArrayList; import java.util.HashSet; import javax.swing.GroupLayout; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; public class Misc { public static final double EPSILON = Math.pow(2.0, -23); public static void thresholdFlexible(byte[] image, int width, int height, int low, int high) { if (low > high) { int temp = low; low = high; high = temp; } int area = width * height; for (int i = 0; i < area; i++) { int pixel = image[i]; // pixel > low && pixel <= high -> white // pixel <= low || pixel > high -> black // this should probably [low, high] not (low, high], but the original behaviour must stay the same image[i] = (byte)(((pixel - (high+1)) >> 31) & ((low - pixel) >> 31)); } } public static long countForegroundPixels(byte[] image, int width, int height) { long count = 0; final int area = width * height; for (int i = 0; i < area; i++) { long pixel = image[i]; count -= (image[i] ^ (image[i] + 1L)) >> 63L; } return count; } public static int roundIntegerToNearestUpperTenth(int a) { int remainder = a % 10; while(remainder != 0) remainder = ++a % 10; return a; } public static boolean isGrayscale(int argb) { int r = (argb >> 16) & 0xff; int g = (argb >> 8) & 0xff; int b = argb & 0xff; return r == g && g == b; } public static int getAnInt(String str) { int n = 0; int len = str.length(); for (int i = 0; i < len; i++) { int c = str.codePointAt(i); if (c >= 0x30 && c <= 0x39) n = n * 10 + (c - 0x30); else break; } return n; } public static int[] getSomeInts(String str) { IntVector numbers = new IntVector(); boolean wasNum = false; boolean isNeg = false; int n = 0; int len = str.length(); for (int i = 0; i < len; i++) { int c = str.codePointAt(i); if (c >= 0x30 && c <= 0x39) { n = n * 10 + (c - 0x30); wasNum = true; } else if (!wasNum && c == '-') { isNeg = true; } else { if (wasNum) numbers.add(isNeg ? -n : n); n = 0; wasNum = false; isNeg = false; } } if (wasNum) numbers.add(isNeg ? -n : n); return numbers.copy(); } public static double[] getSomeDoubles(String str) { IntVector numbers = new IntVector(); boolean wasNum = false; boolean isNeg = false; int mode = 0; int[] nums = new int[3]; int len = str.length(); for (int i = 0; i < len; i++) { int c = str.codePointAt(i); if (c >= 0x30 && c <= 0x39) { if (nums[mode] <= 214748363) nums[mode] = nums[mode] * 10 + (c - 0x30); wasNum = true; } else if (!wasNum && c == '-') { isNeg = true; } else { if (wasNum) { nums[mode] = nums[mode] * 2 + (isNeg ? 1 : 0); isNeg = false; if (mode >= 2 || (mode == 1 && c != 'e' && c != 'E') || (mode == 0 && c != '.')) { numbers.add(nums); nums[0] = 0; nums[1] = 0; nums[2] = 0; mode = 0; } else { mode++; } } wasNum = false; } } if (wasNum) { nums[mode] = nums[mode] * 2 + (isNeg ? 1 : 0); numbers.add(nums); } double[] values = new double[numbers.size / 3]; for (int i = 0; i < numbers.size-2; i += 3) { boolean isNegValue = ((numbers.buf[i] | numbers.buf[i+1]) & 1) != 0; boolean isNegExp = (numbers.buf[i+2] & 1) != 0; int frac = numbers.buf[i+1] >> 1; int f = frac; int fDigits = 0; boolean seenNonZero = false; while (f > 0) { if (f % 10 != 0) seenNonZero = true; if (!seenNonZero) frac /= 10; else fDigits++; f /= 10; } double v = (double)(numbers.buf[i] >> 1); v += (double)frac * Math.pow(10.0, -fDigits); v *= isNegValue ? -1.0 : 1.0; double exp = (double)(numbers.buf[i+2] >> 1); v *= Math.pow(10.0, isNegExp ? -exp : exp); values[i/3] = v; } return values; } public static String formatIntVecTwoPointArray(int[] array) { if (array == null || array.length == 0) return ""; ByteVectorOutputStream sb = new ByteVectorOutputStream(); for (int i = 0; i < array.length / 2; i++) { if (i > 0) sb.add(", "); sb.add('('); sb.add("" + array[2*i]); sb.add(','); sb.add("" + array[2*i+1]); sb.add(')'); } return sb.toString(); } public static String formatIntArray(int[] array, String defaultValue) { if (array == null || array.length == 0) return defaultValue; StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i != 0) sb.append(", "); sb.append("" + array[i]); } return sb.toString(); } public static String formatDoubleArray(double[] array, String defaultValue) { if (array == null || array.length == 0) return defaultValue; StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i != 0) sb.append(", "); sb.append(formatDouble(array[i])); } return sb.toString(); } public static String formatDouble(double value) { String str = "" + value; if (str.endsWith(".0")) return str.substring(0, str.length() - 2); return str; } public static String formatDouble(double value, int nFracDigits) { String str = "" + value; if (str.endsWith(".0")) return str.substring(0, str.length() - 2); int dotIndex = str.indexOf('.'); if (dotIndex < 0) return str; int strLen = str.length(); if (dotIndex + nFracDigits + 1 < strLen) return str.substring(0, dotIndex + nFracDigits + 1); return str; } public static float getHue(int rgb) { int r = (rgb >> 16) & 0xff; int g = (rgb >> 8) & 0xff; int b = rgb & 0xff; float max = Math.max(Math.max(r, g), b); float dMaxMin = max - Math.min(Math.min(r, g), b); // prevent divide by zero if (dMaxMin == 0f) return Float.NaN; float hue = 0f; if (max == (float)r) hue = (g - b) / dMaxMin; else if (max == (float)g) hue = 2f + (b - r) / dMaxMin; else hue = 4f + (r - g) / dMaxMin; return hue; } public static HashSet makeHashSetFromStringArray(String[] array) { HashSet c = new HashSet<>(); for (String s : array) c.add(s); return c; } // TODO: something more sophisticated public static boolean shouldPersistField(Field f) { return !f.getName().equals("inputImagePaths"); } // TODO: also something more sophisticated public static boolean isValidPath(String path) { return path != null && path.length() > 0; } public static int getFileNameOffset(String path) { int lastSlash = path.lastIndexOf('/'); if (lastSlash < 0) lastSlash = path.lastIndexOf('\\'); // if 'path' contains neither a '/' nor a '\', then lastSlash will be -1, which suits us nicely. return lastSlash + 1; } public static String getExtension(File f) { String ext = null; String s = f.getName(); int i = s.lastIndexOf('.'); if (i > 0 && i < s.length() - 1) { ext = s.substring(i + 1).toLowerCase(); } return ext; } public static String[] splitPaths(String blob, char charSplit, char charEscape) { ArrayList paths = new ArrayList<>(); int len = blob.length(); int start = 0; char prev = '\0'; for (int i = 0; i < len; i++) { char c = blob.charAt(i); if (c == charSplit && prev != charEscape) { paths.add(blob.substring(start, i)); start = i+1; } } if (start != len) paths.add(blob.substring(start, len)); return paths.toArray(new String[0]); } public static String decideBackupFileName(String absPath, String ext) { int lastDot = absPath.lastIndexOf('.'); String path = (lastDot > 0 && (lastDot > absPath.lastIndexOf('/') || lastDot > absPath.lastIndexOf('\\'))) ? absPath.substring(0, lastDot) : absPath; ext = ext.charAt(0) == '.' ? ext.substring(1) : ext; String newPath = path + ".bak." + ext; int counter = 1; while (new File(newPath).exists()) newPath = path + ".bak" + (++counter) + "." + ext; return newPath; } public static String addSeparator(String path) { if (path == null || path.length() == 0) return path; // Not sure why this method was designed to modify the input then return it as the output. // Since String is an Object, thus a reference type, // the return value will always be the same as the input after this method is called, because it *is* the input, now modified. if (!path.endsWith(File.separator) && !path.endsWith("/")) path += path.contains(File.separator) ? File.separator : "/"; return path; } public static double parseDouble(String text, double defaultValue) { try { return Double.parseDouble(text); } catch (Exception ignored) {} return defaultValue; } public static int parseInt(String text, int defaultValue) { try { return Integer.parseInt(text); } catch (Exception ignored) {} return defaultValue; } public static boolean isDoubleArraySimilar(double[] a, double[] b) { boolean aIsEmpty = a == null || a.length == 0; boolean bIsEmpty = b == null || b.length == 0; if (aIsEmpty) return bIsEmpty; if (bIsEmpty) return aIsEmpty; if (a.length != b.length) return false; int len = a.length; for (int i = 0; i < len; i++) if (Math.abs(a[i] - b[i]) >= EPSILON) return false; return true; } public static boolean isIntArrayIdentical(int[] a, int[] b) { boolean aIsEmpty = a == null || a.length == 0; boolean bIsEmpty = b == null || b.length == 0; if (aIsEmpty) return bIsEmpty; if (bIsEmpty) return aIsEmpty; if (a.length != b.length) return false; int len = a.length; for (int i = 0; i < len; i++) if (a[i] != b[i]) return false; return true; } public static ByteVectorOutputStream readFullyAsVector(InputStream stream) throws IOException { final int chunkSize = 4096; ByteVectorOutputStream vec = new ByteVectorOutputStream(chunkSize); int off = 0; while (true) { int res = stream.read(vec.buf, off, chunkSize); if (res <= 0) break; off += res; vec.resize(off + chunkSize); } vec.resize(off); return vec; } public static void setNewFontSizeOn(JComponent ui, int newSize) { Font font = ui.getFont(); ui.setFont(new Font(font.getName(), font.getStyle(), newSize)); } public static void setNewFontStyleOn(JComponent ui, int newStyle) { Font font = ui.getFont(); ui.setFont(new Font(font.getName(), newStyle, font.getSize())); } public static JFileChooser createFileChooser() { JFileChooser fc = new JFileChooser(); fc.setPreferredSize(new Dimension(800, 500)); return fc; } public static void addImageFileFilters(JFileChooser fc) { fc.addChoosableFileFilter(new SimpleFileFilter("JPEG file", "jpg", "jpeg")); fc.addChoosableFileFilter(new SimpleFileFilter("GIF file", "gif")); fc.addChoosableFileFilter(new SimpleFileFilter("PNG file", "png")); fc.addChoosableFileFilter(new SimpleFileFilter("BMP file", "bmp")); fc.addChoosableFileFilter(new SimpleFileFilter("TIFF file", "tiff", "tif")); fc.addChoosableFileFilter(new SimpleFileFilter("PPM file", "ppm")); } public static void showDialogBox(String title, String message) { JOptionPane.showMessageDialog( JOptionPane.getRootFrame(), message, title, 0 ); } public static void showExceptionInDialogBox(Throwable t) { if (t == null) return; String name = t.getClass().getName(); String message = buildDialogMessageFromException(t); showDialogBox(name, message); } public static String buildDialogMessageFromException(Throwable t) { String message = t.getMessage(); StackTraceElement[] st = t.getStackTrace(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < st.length; i++) { String className = st[i].getClassName(); if (!className.startsWith("java.") && !className.startsWith("javax.")) { sb.append("\n"); sb.append(st[i].toString()); } } String exSource = sb.toString(); if (message == null || message.length() == 0) message = exSource; else message += exSource; return message; } public static GroupLayout.SequentialGroup arrangeParallelEntries( NumberEntry a, NumberEntry b, GroupLayout layout, GroupLayout.SequentialGroup sequentialGroup ) { sequentialGroup .addGroup(layout.createParallelGroup() .addComponent(a.cb) .addComponent(b.cb) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING) .addComponent(a.units) .addComponent(b.units) ) .addGap(4) .addGroup(layout.createParallelGroup() .addComponent(a.tf) .addComponent(b.tf) ); if (a instanceof SizeColorEntry && b instanceof SizeColorEntry) sequentialGroup.addGroup(layout.createParallelGroup() .addComponent(((SizeColorEntry)a).colorElem.panel, 20, 30, 30) .addComponent(((SizeColorEntry)b).colorElem.panel, 20, 30, 30) ); return sequentialGroup; } public static ArrayList openSpreadsheetForAppending( String[] outStrings, String existingXlsxFile, String defaultPath, JFrame parentFrame ) { File xlsxFile; if (existingXlsxFile == null || existingXlsxFile.length() == 0) { JFileChooser fc = createFileChooser(); fc.setDialogTitle("Append to Excel spreadsheet"); fc.setDialogType(JFileChooser.SAVE_DIALOG); fc.setCurrentDirectory(new File(defaultPath)); fc.setFileFilter(new SimpleFileFilter("Excel Spreadsheet", "xls", "xlsx")); if (fc.showSaveDialog(parentFrame) != 0) { outStrings[0] = null; return null; } xlsxFile = fc.getSelectedFile(); if (!xlsxFile.getName().contains(".")) xlsxFile = new File(xlsxFile.getAbsolutePath() + ".xlsx"); defaultPath = fc.getCurrentDirectory().getAbsolutePath(); } else { if (!existingXlsxFile.contains(".")) existingXlsxFile += ".xlsx"; xlsxFile = new File(existingXlsxFile); defaultPath = xlsxFile.getParent(); } String xlsxPath = xlsxFile.getAbsolutePath(); outStrings[0] = xlsxPath; outStrings[1] = defaultPath; ArrayList sheets = null; if (xlsxFile.exists()) { try { sheets = XlsxReader.loadXlsxFromFile(xlsxPath); } catch (IOException ignored) {} if (sheets == null || sheets.isEmpty() || (sheets.get(0).flags & (1 << 31)) == 0) { try { Files.copy( xlsxFile.toPath(), new File(decideBackupFileName(xlsxPath, "xlsx")).toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES ); } catch (IOException ignored) {} } } if (sheets == null) sheets = new ArrayList(); return sheets; } }