/** * This file is part of VisiCut. * Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de> * RWTH Aachen University - 52062 Aachen, Germany * * VisiCut 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. * * VisiCut 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 VisiCut. If not, see <http://www.gnu.org/licenses/>. **/ package com.t_oster.visicut.misc; import java.awt.Color; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.MalformedURLException; import java.net.NetworkInterface; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; /** * This class contains frequently used conversion methods * * @author Thomas Oster <thomas.oster@rwth-aachen.de> */ public class Helper { public static List<String> findVisiCamInstances() { List<String> result = new LinkedList<String>(); // Find the server using UDP broadcast try { //Open a random port to send the package DatagramSocket c = new DatagramSocket(); c.setBroadcast(true); byte[] sendData = "VisiCamDiscover".getBytes(); //Try the 255.255.255.255 first try { DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("255.255.255.255"), 8888); c.send(sendPacket); } catch (Exception e) { } // Broadcast the message over all the network interfaces Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface networkInterface = interfaces.nextElement(); if (networkInterface.isLoopback() || !networkInterface.isUp()) { continue; // Don't want to broadcast to the loopback interface } for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { InetAddress broadcast = interfaceAddress.getBroadcast(); if (broadcast == null) { continue; } // Send the broadcast package! try { DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, broadcast, 8888); c.send(sendPacket); } catch (Exception e) { } } } //Wait for a response byte[] recvBuf = new byte[15000]; c.setSoTimeout(3000); while(true) { DatagramPacket receivePacket = new DatagramPacket(recvBuf, recvBuf.length); try { c.receive(receivePacket); //Check if the message is correct String message = new String(receivePacket.getData()).trim(); //Close the port! c.close(); if (message.startsWith("http")) { result.add(message); } } catch (SocketTimeoutException e) { break; } } } catch (IOException ex) { } return result; } public static Double evaluateExpression(String expr) { expr = expr.replace(",", "."); try { ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine engine = mgr.getEngineByName("JavaScript"); expr = engine.eval(expr).toString(); } catch (Exception e) { //e.printStackTrace(); } return Double.parseDouble(expr); } public static double angle2degree(double angle) { double w = -(angle * 180d / Math.PI); if (w < 0) { w+=360; } return w; } public static double degree2angle(double degree) { return -Math.PI*degree/180d; } /** * Compute the rotation angle of an affine transformation. * Counter-clockwise rotation is considered positive. * * method taken from http://javagraphics.blogspot.com/ * * @return rotation angle in radians (beween -pi and pi), * or NaN if the transformation is bogus. */ public static double getRotationAngle(AffineTransform transform) { transform = (AffineTransform) transform.clone(); // Eliminate any post-translation transform.preConcatenate(AffineTransform.getTranslateInstance( -transform.getTranslateX(), -transform.getTranslateY())); Point2D p1 = new Point2D.Double(1,0); p1 = transform.transform(p1,p1); return Math.atan2(p1.getY(),p1.getX()); } /** * Returns the distance between two Rectangles * @param r first rectangle * @param q second rectangle * @return Distance between two rectangles */ public static double distance(Rectangle2D r, Rectangle2D q) { double qx0 = q.getX(); double qy0 = q.getY(); double qx1 = q.getX()+q.getWidth(); double qy1 = q.getY()+q.getHeight(); double rx0 = r.getX(); double ry0 = r.getY(); double rx1 = r.getX()+r.getWidth(); double ry1 = r.getY()+r.getHeight(); //Check for Overlap if (qx0 <= rx1 && qy0 <= ry1 && rx0 <= qx1 && ry0 <= qy1) { return 0; } double d = 0; if (rx0 > qx1) { d += (rx0 - qx1) * (rx0 - qx1); } else if (qx0 > rx1) { d += (qx0 - rx1) * (qx0 - rx1); } if (ry0 > qy1) { d += (ry0 - qy1) * (ry0 - qy1); } else if (qy0 > ry1) { d += (qy0 - ry1) * (qy0 - ry1); } return Math.sqrt(d); } public static boolean isMacOS() { return System.getProperty("os.name").toLowerCase().contains("mac"); } public static boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("windows"); } public static boolean isLinux() { return System.getProperty("os.name").toLowerCase().contains("linux"); } public static boolean isWindowsXP() { return isWindows() && System.getProperty("os.name").toLowerCase().contains("xp"); } public static void installInkscapeExtension() throws FileNotFoundException, IOException { File src = new File(getVisiCutFolder(), "inkscape_extension"); if (!src.exists() || !src.isDirectory()) { throw new FileNotFoundException("Not a directory: "+src); } File trg = null; if (isWindowsXP()) { trg = new File(new File(FileUtils.getUserDirectory(), ".inkscape"), "extensions"); } else if (isWindows()) { trg = new File(new File(new File(FileUtils.getUserDirectory(), "Application Data"), "inkscape"), "extensions"); } else { trg = new File(new File(new File(FileUtils.getUserDirectory(), ".config"), "inkscape"), "extensions"); } if (!trg.exists() && !trg.mkdirs()) { throw new FileNotFoundException("Can't create directory: "+trg); } for (File f :src.listFiles()) { if ("visicut_export.py".equals(f.getName())) { File target = new File(trg, "visicut_export.py"); BufferedReader r = new BufferedReader(new FileReader(f)); BufferedWriter w = new BufferedWriter(new FileWriter(target)); String line = r.readLine(); while (line != null) { if ("VISICUTBIN=\"visicut\"".equals(line)) { if (isMacOS()) { line = "VISICUTBIN=\""+new File(getVisiCutFolder(), "VisiCut.MacOS").getAbsolutePath()+"\""; } else if (isWindows()) { line = "VISICUTBIN=\""+new File(getVisiCutFolder(), "VisiCut.exe").getAbsolutePath()+"\""; } else { line = "VISICUTBIN=\""+new File(getVisiCutFolder(), "VisiCut.Linux").getAbsolutePath()+"\""; } } w.write(line); w.newLine(); line = r.readLine(); } w.flush(); w.close(); r.close(); } else if (f.getName().toLowerCase().endsWith("inx") || f.getName().toLowerCase().endsWith("py")) { FileUtils.copyFileToDirectory(f, trg); } } } public static void installIllustratorScript() throws IOException { String errors = ""; for (File dir : new File[]{ new File("/Applications/Adobe Illustrator CS3/Presets"), new File("/Applications/Adobe Illustrator CS4/Presets"), new File("/Applications/Adobe Illustrator CS5/Presets"), new File("/Applications/Adobe Illustrator CS4/Presets.localized"), new File("/Applications/Adobe Illustrator CS5/Presets.localized") }) { if (dir.exists() && dir.isDirectory()) { for (File d : dir.listFiles()) { if (!d.isDirectory()) { continue; } if (!"Scripts".equals(d.getName())) { d = new File(d, "Scripts"); if (!d.exists() || !d.isDirectory()) { continue; } } try { FileUtils.copyFileToDirectory(getIllustratorScript(), d); } catch (IOException ex) { errors += "Can't copy to "+d.getAbsolutePath()+"\n"; } } } } if (!"".equals(errors)) { throw new IOException(errors); } } public static File getVisiCutFolder() { try { String path = Helper.class.getProtectionDomain().getCodeSource().getLocation().getPath(); if (path == null) { return null; } String decodedPath = URLDecoder.decode(path, "UTF-8"); File folder = new File(decodedPath); return folder.isDirectory() ? folder : folder.getParentFile(); } catch (UnsupportedEncodingException ex) { Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); } return null; } protected static File basePath; public static File getBasePath() { if (basePath == null) { basePath = new File(FileUtils.getUserDirectory(), ".visicut"); } return basePath; } public static void setBasePath(File f) { basePath = f; } public static String removeParentPath(File parent, String path) { if (path == null) { return null; } File p = new File(path); File bp = parent; String result = p.getName(); while (p.getParentFile() != null) { p = p.getParentFile(); if (p.getAbsolutePath().equals(bp.getAbsolutePath())) { return result; } result = p.getName() + "/" + result; } return path; } public static String addParentPath(File parent, String path) { if (path == null) { return null; } if (!(new File(path).isAbsolute())) { return new File(parent, path).getAbsolutePath(); } return path; } /** * If the given path is a successor of the parent-path, * only the relative path is given back. * Otherwise the path is not modified * @param path * @return */ public static String removeBasePath(String path) { return removeParentPath(getBasePath(), path); } /** * If the given path is relative, the base-path is prepended * @param parent * @param path * @return */ public static String addBasePath(String path) { return addParentPath(getBasePath(), path); } /** * Generates an HTML img-Tag for the given file with given size * @param f * @param width * @param height * @return */ public static String imgTag(URL u, int width, int height) { String size = width > 0 && height > 0 ? "width=" + width + " height=" + height : ""; return "<img " + size + " src=\"" + u + "\"/>"; } /** * Generates an HTML img-Tag for the given file with given size * @param f * @param width * @param height * @return */ public static String imgTag(File f, int width, int height) { try { return imgTag(f.toURI().toURL(), width, height); } catch (MalformedURLException ex) { Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); String size = width > 0 && height > 0 ? "width=" + width + " height=" + height : ""; return "<img " + size + " src=\"file://" + f.getAbsolutePath() + "\"/>"; } } /** * Returns an AffineTransform, which transformes src to dest * and constists of a scale and translate component * @param src * @param dest * @return */ public static AffineTransform getTransform(Rectangle2D src, Rectangle2D dest) { AffineTransform scale = AffineTransform.getScaleInstance(dest.getWidth() / src.getWidth(), dest.getHeight() / src.getHeight()); Point2D scaled = scale.transform(new Point.Double(src.getX(), src.getY()), null); AffineTransform result = AffineTransform.getTranslateInstance(dest.getX() - scaled.getX(), dest.getY() - scaled.getY()); result.concatenate(scale); return result; } public static Point toPoint(Point2D p) { return new Point((int) p.getX(), (int) p.getY()); } public static Rectangle toRect(Rectangle2D src) { if (src == null) { return new Rectangle(0, 0, 0, 0); } return new Rectangle((int) src.getX(), (int) src.getY(), (int) src.getWidth(), (int) src.getHeight()); } /** * Returns the smalles BoundingBox, which contains a number of Poins * @param points * @return */ public static Rectangle2D smallestBoundingBox(java.awt.Point.Double[] points) { double minX = points[0].x; double minY = points[0].y; double maxX = points[0].x; double maxY = points[0].y; for (java.awt.Point.Double p:points) { if (p.x < minX) {minX = p.x;} if (p.y < minY) {minY = p.y;} if (p.x > maxX) {maxX = p.x;} if (p.y > maxY) {maxY = p.y;} } return new Rectangle.Double(minX, minY, maxX-minX, maxY - minY); } public static Rectangle2D smallestBoundingBox(Shape s, AffineTransform t) { double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; PathIterator pi = s.getPathIterator(t, 1); double[] last = null; boolean first = true; while (!pi.isDone()) { double[] d = new double[8]; switch(pi.currentSegment(d)) { case PathIterator.SEG_LINETO: { if (last != null) { if (first) { minX = last[0]; maxX = last[0]; minY = last[1]; maxY = last[1]; first = false; } else { if (last[0] < minX) { minX = last[0]; } if (last[0] > maxX) { maxX = last[0]; } if (last[1] < minY) { minY = last[1]; } if (last[1] > maxY) { maxY = last[1]; } } } if (first) { minX = d[0]; maxX = d[0]; minY = d[1]; maxY = d[1]; first = false; } else { if (d[0] < minX) { minX = d[0]; } if (d[0] > maxX) { maxX = d[0]; } if (d[1] < minY) { minY = d[1]; } if (d[1] > maxY) { maxY = d[1]; } } break; } case PathIterator.SEG_MOVETO: { last = d; break; } } pi.next(); } return new Rectangle.Double(minX, minY, maxX-minX, maxY - minY); } /** * Returns a rectangle (parralel to x and y axis), which contains * the given rectangle after the given transform. If the transform * contains a rotation, the resulting rectangle is the smallest bounding-box * @param src * @param at * @return */ public static Rectangle2D transform(Rectangle2D src, AffineTransform at) { if (at == null) { return src; } else { java.awt.Point.Double[] points = new java.awt.Point.Double[4]; points[0] = new java.awt.Point.Double(src.getX(), src.getY()); points[1] = new java.awt.Point.Double(src.getX(), src.getY()+src.getHeight()); points[2] = new java.awt.Point.Double(src.getX()+src.getWidth(), src.getY()); points[3] = new java.awt.Point.Double(src.getX()+src.getWidth(), src.getY()+src.getHeight()); for (int i=0;i<4;i++) { at.transform(points[i], points[i]); } return smallestBoundingBox(points); } } public static String toHtmlRGB(Color col) { String r = Integer.toHexString(col.getRed()); String g = Integer.toHexString(col.getGreen()); String b = Integer.toHexString(col.getBlue()); return "#" + (r.length() == 1 ? "0" + r : r) + (g.length() == 1 ? "0" + g : g) + (b.length() == 1 ? "0" + b : b); } public static Color fromHtmlRGB(String rgb) { int r = Integer.parseInt(rgb.substring(1, 3), 16); int g = Integer.parseInt(rgb.substring(3, 5), 16); int b = Integer.parseInt(rgb.substring(5, 7), 16); return new Color(r,g,b); } /** * Returns the given time in s as HH:MM:SS * @param estimateTime * @return */ public static String toHHMMSS(int estimateTime) { String result = ""; int v = estimateTime / 3600; result += v < 10 ? "0" + v + ":" : "" + v + ":"; estimateTime = estimateTime % 3600; v = estimateTime / 60; result += v < 10 ? "0" + v + ":" : "" + v + ":"; estimateTime = estimateTime % 60; v = estimateTime; result += v < 10 ? "0" + v : "" + v; return result; } public static boolean isInkscapeExtensionInstallable() { File is = new File(getVisiCutFolder(), "inkscape_extension"); return is.exists() && is.isDirectory(); } private static File getIllustratorScript() { return new File(new File(getVisiCutFolder(), "illustrator_script"), "OpenWithVisiCut.scpt"); } public static boolean isIllustratorScriptInstallable() { return isMacOS() && getIllustratorScript().exists(); } public static String allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-"; /** * Converts a string into a valid path name * @param name * @return */ public static String toPathName(String name) { String result = ""; for (char c : name.toCharArray()) { if (!allowedChars.contains(""+c)) { result += "_"+((int) c)+"_"; } else { result += c; } } return result; } /** * Converts a converted filename from toPathName() * back to the original string * @param name * @return */ public static String fromPathName(String name) { String result = ""; String index = null; for (char c : name.toCharArray()) { if (index == null) { if (c == '_') { index = ""; } else { result += c; } } else { if (c == '_') { result += (char) Integer.parseInt(index); index = null; } else { index += c; } } } return result; } }