|
| 1 | +package de.lighti.clipper.console; |
| 2 | + |
| 3 | +import java.awt.Color; |
| 4 | +import java.io.BufferedReader; |
| 5 | +import java.io.BufferedWriter; |
| 6 | +import java.io.File; |
| 7 | +import java.io.FileReader; |
| 8 | +import java.io.FileWriter; |
| 9 | +import java.io.IOException; |
| 10 | +import java.util.Locale; |
| 11 | +import java.util.Random; |
| 12 | +import java.util.StringTokenizer; |
| 13 | + |
| 14 | +import de.lighti.clipper.Clipper; |
| 15 | +import de.lighti.clipper.Clipper.ClipType; |
| 16 | +import de.lighti.clipper.Clipper.PolyFillType; |
| 17 | +import de.lighti.clipper.Clipper.PolyType; |
| 18 | +import de.lighti.clipper.DefaultClipper; |
| 19 | +import de.lighti.clipper.Path; |
| 20 | +import de.lighti.clipper.Paths; |
| 21 | +import de.lighti.clipper.Point.LongPoint; |
| 22 | + |
| 23 | +public class Program { |
| 24 | + |
| 25 | + static Path IntsToPolygon( int[] ints ) { |
| 26 | + final int len1 = ints.length / 2; |
| 27 | + final Path result = new Path( len1 ); |
| 28 | + for (int i = 0; i < len1; i++) { |
| 29 | + result.add( new LongPoint( ints[i * 2], ints[i * 2 + 1] ) ); |
| 30 | + } |
| 31 | + return result; |
| 32 | + } |
| 33 | + |
| 34 | + static boolean LoadFromFile( String filename, Paths ppg, int dec_places ) throws IOException { |
| 35 | + return LoadFromFile( filename, ppg, dec_places, 0, 0 ); |
| 36 | + } |
| 37 | + |
| 38 | + static boolean LoadFromFile( String filename, Paths ppg, int dec_places, int xOffset, int yOffset ) throws IOException { |
| 39 | + final double scaling = Math.pow( 10, dec_places ); |
| 40 | + |
| 41 | + ppg.clear(); |
| 42 | + if (!new File( filename ).exists()) { |
| 43 | + return false; |
| 44 | + } |
| 45 | + final String delimiters = ", "; |
| 46 | + final BufferedReader sr = new BufferedReader( new FileReader( filename ) ); |
| 47 | + try { |
| 48 | + String line; |
| 49 | + if ((line = sr.readLine()) == null) { |
| 50 | + return false; |
| 51 | + } |
| 52 | + final int polyCnt = Integer.parseInt( line ); |
| 53 | + if (polyCnt < 0) { |
| 54 | + return false; |
| 55 | + } |
| 56 | + |
| 57 | + for (int i = 0; i < polyCnt; i++) { |
| 58 | + if ((line = sr.readLine()) == null) { |
| 59 | + return false; |
| 60 | + } |
| 61 | + final int vertCnt = Integer.parseInt( line ); |
| 62 | + if (vertCnt < 0) { |
| 63 | + return false; |
| 64 | + } |
| 65 | + final Path pg = new Path( vertCnt ); |
| 66 | + ppg.add( pg ); |
| 67 | + if (scaling > 0.999 & scaling < 1.001) { |
| 68 | + for (int j = 0; j < vertCnt; j++) { |
| 69 | + int x, y; |
| 70 | + if ((line = sr.readLine()) == null) { |
| 71 | + return false; |
| 72 | + } |
| 73 | + final StringTokenizer tokens = new StringTokenizer( line, delimiters ); |
| 74 | + |
| 75 | + if (tokens.countTokens() < 2) { |
| 76 | + return false; |
| 77 | + } |
| 78 | + |
| 79 | + x = Integer.parseInt( tokens.nextToken() ); |
| 80 | + y = Integer.parseInt( tokens.nextToken() ); |
| 81 | + |
| 82 | + x = x + xOffset; |
| 83 | + y = y + yOffset; |
| 84 | + pg.add( new LongPoint( x, y ) ); |
| 85 | + } |
| 86 | + } |
| 87 | + else { |
| 88 | + for (int j = 0; j < vertCnt; j++) { |
| 89 | + double x, y; |
| 90 | + if ((line = sr.readLine()) == null) { |
| 91 | + return false; |
| 92 | + } |
| 93 | + final StringTokenizer tokens = new StringTokenizer( line, delimiters ); |
| 94 | + |
| 95 | + if (tokens.countTokens() < 2) { |
| 96 | + return false; |
| 97 | + } |
| 98 | + x = Double.parseDouble( tokens.nextToken() ); |
| 99 | + y = Double.parseDouble( tokens.nextToken() ); |
| 100 | + |
| 101 | + x = x * scaling + xOffset; |
| 102 | + y = y * scaling + yOffset; |
| 103 | + pg.add( new LongPoint( (int) Math.round( x ), (int) Math.round( y ) ) ); |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + return true; |
| 108 | + } |
| 109 | + finally { |
| 110 | + sr.close(); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + public static void main( String[] args ) { |
| 115 | + //Enforce a US locale, or the SVG data will have decimals in form ##,## in countries |
| 116 | + //where ',' is the decimal separator. SVG requires a '.' instead |
| 117 | + Locale.setDefault( Locale.US ); |
| 118 | + |
| 119 | + if (args.length < 5) { |
| 120 | + final String appname = System.getProperty( "sun.java.command" ); |
| 121 | + System.out.println( "" ); |
| 122 | + System.out.println( "Usage:" ); |
| 123 | + System.out.println( String.format( " %1$s CLIPTYPE s_file c_file INPUT_DEC_PLACES SVG_SCALE [S_FILL, C_FILL]", appname ) ); |
| 124 | + System.out.println( " where ..." ); |
| 125 | + System.out.println( " CLIPTYPE = INTERSECTION|UNION|DIFFERENCE|XOR" ); |
| 126 | + System.out.println( " FILLMODE = NONZERO|EVENODD" ); |
| 127 | + System.out.println( " INPUT_DEC_PLACES = signific. decimal places for subject & clip coords." ); |
| 128 | + System.out.println( " SVG_SCALE = scale of SVG image as power of 10. (Fractions are accepted.)" ); |
| 129 | + System.out.println( " both S_FILL and C_FILL are optional. The default is EVENODD." ); |
| 130 | + System.out.println( "Example:" ); |
| 131 | + System.out.println( " Intersect polygons, rnd to 4 dec places, SVG is 1/100 normal size ..." ); |
| 132 | + System.out.println( String.format( " %1$s INTERSECTION subj.txt clip.txt 0 0 NONZERO NONZERO", appname ) ); |
| 133 | + return; |
| 134 | + } |
| 135 | + |
| 136 | + ClipType ct; |
| 137 | + switch (args[0].toUpperCase()) { |
| 138 | + case "INTERSECTION": |
| 139 | + ct = ClipType.INTERSECTION; |
| 140 | + break; |
| 141 | + case "UNION": |
| 142 | + ct = ClipType.UNION; |
| 143 | + break; |
| 144 | + case "DIFFERENCE": |
| 145 | + ct = ClipType.DIFFERENCE; |
| 146 | + break; |
| 147 | + case "XOR": |
| 148 | + ct = ClipType.XOR; |
| 149 | + break; |
| 150 | + default: |
| 151 | + System.out.println( "Error: invalid operation - " + args[0] ); |
| 152 | + return; |
| 153 | + } |
| 154 | + |
| 155 | + final String subjFilename = args[1]; |
| 156 | + final String clipFilename = args[2]; |
| 157 | + if (!new File( subjFilename ).exists()) { |
| 158 | + System.out.println( "Error: file - " + subjFilename + " - does not exist." ); |
| 159 | + return; |
| 160 | + } |
| 161 | + if (!new File( clipFilename ).exists()) { |
| 162 | + System.out.println( "Error: file - " + clipFilename + " - does not exist." ); |
| 163 | + return; |
| 164 | + } |
| 165 | + |
| 166 | + int decimal_places = Integer.parseInt( args[3] ); |
| 167 | + if (decimal_places < 0) { |
| 168 | + System.out.println( "Error: invalid number of decimal places - " + args[3] ); |
| 169 | + return; |
| 170 | + } |
| 171 | + if (decimal_places > 8) { |
| 172 | + decimal_places = 8; |
| 173 | + } |
| 174 | + else if (decimal_places < 0) { |
| 175 | + decimal_places = 0; |
| 176 | + } |
| 177 | + |
| 178 | + double svg_scale = Double.parseDouble( args[4] ); |
| 179 | + if (svg_scale < 0) { |
| 180 | + System.out.println( "Error: invalid value for SVG_SCALE - " + args[4] ); |
| 181 | + return; |
| 182 | + } |
| 183 | + if (svg_scale < -18) { |
| 184 | + svg_scale = -18; |
| 185 | + } |
| 186 | + else if (svg_scale > 18) { |
| 187 | + svg_scale = 18; |
| 188 | + } |
| 189 | + svg_scale = Math.pow( 10, svg_scale - decimal_places );//nb: also compensate for decimal places |
| 190 | + |
| 191 | + PolyFillType pftSubj = PolyFillType.EVEN_ODD; |
| 192 | + PolyFillType pftClip = PolyFillType.EVEN_ODD; |
| 193 | + if (args.length > 6) { |
| 194 | + switch (args[5].toUpperCase()) { |
| 195 | + case "EVENODD": |
| 196 | + pftSubj = PolyFillType.EVEN_ODD; |
| 197 | + break; |
| 198 | + case "NONZERO": |
| 199 | + pftSubj = PolyFillType.NON_ZERO; |
| 200 | + break; |
| 201 | + default: |
| 202 | + System.out.println( "Error: invalid cliptype - " + args[5] ); |
| 203 | + return; |
| 204 | + } |
| 205 | + switch (args[6].toUpperCase()) { |
| 206 | + case "EVENODD": |
| 207 | + pftClip = PolyFillType.EVEN_ODD; |
| 208 | + break; |
| 209 | + case "NONZERO": |
| 210 | + pftClip = PolyFillType.NON_ZERO; |
| 211 | + break; |
| 212 | + default: |
| 213 | + System.out.println( "Error: invalid cliptype - " + args[6] ); |
| 214 | + return; |
| 215 | + } |
| 216 | + } |
| 217 | + try { |
| 218 | + final Paths subjs = new Paths(); |
| 219 | + final Paths clips = new Paths(); |
| 220 | + if (!LoadFromFile( subjFilename, subjs, decimal_places )) { |
| 221 | + System.out.println( "Error processing subject polygons file - " + subjFilename ); |
| 222 | + OutputFileFormat(); |
| 223 | + return; |
| 224 | + } |
| 225 | + if (!LoadFromFile( clipFilename, clips, decimal_places )) { |
| 226 | + System.out.println( "Error processing clip polygons file - " + clipFilename ); |
| 227 | + OutputFileFormat(); |
| 228 | + return; |
| 229 | + } |
| 230 | + |
| 231 | + System.out.println( "wait ..." ); |
| 232 | + final DefaultClipper cp = new DefaultClipper( Clipper.STRICTLY_SIMPLE ); |
| 233 | + cp.addPaths( subjs, PolyType.SUBJECT, true ); |
| 234 | + cp.addPaths( clips, PolyType.CLIP, true ); |
| 235 | + |
| 236 | + final Paths solution = new Paths(); |
| 237 | + //Paths solution = new Paths(); |
| 238 | + if (cp.execute( ct, solution, pftSubj, pftClip )) { |
| 239 | + saveToFile( "solution.txt", solution, decimal_places ); |
| 240 | + |
| 241 | + //solution = Clipper.OffsetPolygons(solution, -4, JoinType.jtRound); |
| 242 | + |
| 243 | + final SVGBuilder svg = new SVGBuilder(); |
| 244 | + svg.style.showCoords = true; |
| 245 | + svg.style.brushClr = new Color( 0, 0, 0x9c, 0x20 ); |
| 246 | + svg.style.penClr = new Color( 0xd3, 0xd3, 0xda ); |
| 247 | + svg.addPaths( subjs ); |
| 248 | + svg.style.brushClr = new Color( 0x20, 0x9c, 0, 0 ); |
| 249 | + svg.style.penClr = new Color( 0xff, 0xa0, 0x7a ); |
| 250 | + svg.addPaths( clips ); |
| 251 | + svg.style.brushClr = new Color( 0x80, 0xff, 0x9c, 0xAA ); |
| 252 | + svg.style.penClr = new Color( 0, 0x33, 0 ); |
| 253 | + svg.addPaths( solution ); |
| 254 | + svg.saveToFile( "solution.svg", svg_scale ); |
| 255 | + |
| 256 | + System.out.println( "finished!" ); |
| 257 | + } |
| 258 | + else { |
| 259 | + System.out.println( "failed!" ); |
| 260 | + } |
| 261 | + } |
| 262 | + catch (final IOException e) { |
| 263 | + System.out.println( "An error occured: " + e.getMessage() ); |
| 264 | + } |
| 265 | + } |
| 266 | + |
| 267 | + static Path MakeRandomPolygon( Random r, int maxWidth, int maxHeight, int edgeCount ) { |
| 268 | + return MakeRandomPolygon( r, maxWidth, maxHeight, edgeCount, 1 ); |
| 269 | + } |
| 270 | + |
| 271 | + static Path MakeRandomPolygon( Random r, int maxWidth, int maxHeight, int edgeCount, int scale ) { |
| 272 | + final Path result = new Path( edgeCount ); |
| 273 | + for (int i = 0; i < edgeCount; i++) { |
| 274 | + result.add( new LongPoint( r.nextInt( maxWidth ) * scale, r.nextInt( maxHeight ) * scale ) ); |
| 275 | + } |
| 276 | + return result; |
| 277 | + } |
| 278 | + |
| 279 | + static void OutputFileFormat() { |
| 280 | + System.out.println( "The expected (text) file format is ..." ); |
| 281 | + System.out.println( "Polygon Count" ); |
| 282 | + System.out.println( "First polygon vertex count" ); |
| 283 | + System.out.println( "first X, Y coordinate of first polygon" ); |
| 284 | + System.out.println( "second X, Y coordinate of first polygon" ); |
| 285 | + System.out.println( "etc." ); |
| 286 | + System.out.println( "Second polygon vertex count (if there is one)" ); |
| 287 | + System.out.println( "first X, Y coordinate of second polygon" ); |
| 288 | + System.out.println( "second X, Y coordinate of second polygon" ); |
| 289 | + System.out.println( "etc." ); |
| 290 | + } |
| 291 | + |
| 292 | + static void saveToFile( String filename, Paths ppg, int dec_places ) throws IOException { |
| 293 | + final double scaling = Math.pow( 10, dec_places ); |
| 294 | + final BufferedWriter writer = new BufferedWriter( new FileWriter( filename ) ); |
| 295 | + try { |
| 296 | + writer.write( String.format( "%d%n", ppg.size() ) ); |
| 297 | + for (final Path pg : ppg) { |
| 298 | + writer.write( String.format( "%d%n", pg.size() ) ); |
| 299 | + for (final LongPoint ip : pg) { |
| 300 | + writer.write( String.format( "%.2f, %.2f%n", ip.getX() / scaling, ip.getY() / scaling ) ); |
| 301 | + } |
| 302 | + } |
| 303 | + } |
| 304 | + finally { |
| 305 | + writer.close(); |
| 306 | + } |
| 307 | + } |
| 308 | +} //class Program |
| 309 | + |
0 commit comments