001package squidpony.squidgrid.mapping;
002
003import squidpony.squidmath.GreasedRegion;
004
005/**
006 * Tools for constructing patterns using box-drawing characters.
007 * <br>
008 * Created by Tommy Ettinger on 1/6/2018.
009 */
010public class LineKit {
011    public static final char[] lightAlt = " ╴╵┘╶─└┴╷┐│┤┌┬├┼".toCharArray(),
012                               heavyAlt = " ╸╹┛╺━┗┻╻┓┃┫┏┳┣╋".toCharArray(),
013                               light    = " ─│┘──└┴│┐│┤┌┬├┼".toCharArray(),
014                               heavy    = " ━┃┛━━┗┻┃┓┃┫┏┳┣╋".toCharArray();
015    //                                     0123456789ABCDEF
016
017    /**
018     * A constant that represents the encoded pattern for a 4x4 square with all lines possible except those that
019     * would extend to touch cells adjacent to the 4x4 area. Meant to restrict cells within the square area by using
020     * bitwise AND with an existing encoded pattern as another long, as with {@code LineKit.interiorSquare & encoded}.
021     * If you limit the area to the square with this, you may sometimes want to add a border, and for that you can use
022     * {@link #exteriorSquare} and bitwise OR that with the restricted area.
023     * <br>This looks like: 
024     * <pre>
025     * "┌┬┬┐"
026     * "├┼┼┤"
027     * "├┼┼┤"
028     * "└┴┴┘"
029     * </pre>
030     */
031    public final static long interiorSquare = 0x3776BFFEBFFE9DDCL,
032    /**
033     * A constant that represents the encoded pattern for a 4x4 square with only the lines along the border. Meant to
034     * either restrict cells to the border by using bitwise AND with an existing encoded pattern as another long, as
035     * with {@code LineKit.exteriorSquare & encoded}, or to add a border to an existing pattern with bitwise OR, as with
036     * {@code LineKit.exteriorSquare | encoded}.
037     * <br>This looks like: 
038     * <pre>
039     * "┌──┐"
040     * "│  │"
041     * "│  │"
042     * "└──┘"
043     * </pre>
044     */
045    exteriorSquare = 0x3556A00AA00A955CL,
046    /**
047     * A constant that represents the encoded pattern for a 4x4 plus pattern with only the lines along the border. This
048     * pattern has no lines in the corners of the 4x4 area, but has some lines in all other cells, though none that
049     * would touch cells adjacent to this 4x4 area. Meant to restrict cells to the border by using bitwise AND with an
050     * existing encoded pattern as another long, as with {@code LineKit.interiorPlus & encoded}.
051     * <br>This looks like: 
052     * <pre>
053     * " ┌┐ "
054     * "┌┼┼┐"
055     * "└┼┼┘"
056     * " └┘ "
057     * </pre>
058     */
059    interiorPlus = 0x03603FF69FFC09C0L,
060    /**
061     * A constant that represents the encoded pattern for a 4x4 plus pattern with only the lines along the border. This
062     * pattern has no lines in the corners of the 4x4 area, but has some lines in all other cells, though none that
063     * would touch cells adjacent to this 4x4 area. Meant to either restrict cells to the border by using bitwise AND
064     * with an existing encoded pattern as another long, as with {@code LineKit.exteriorPlus & encoded}, or to add a
065     * border to an existing pattern with bitwise OR, as with {@code LineKit.exteriorPlus | encoded}.
066     * <br>This looks like: 
067     * <pre>
068     * " ┌┐ "
069     * "┌┘└┐"
070     * "└┐┌┘"
071     * " └┘ "
072     * </pre>
073     */
074    exteriorPlus = 0x03603C96963C09C0L,
075    /**
076     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 square. No lines will touch
077     * the upper or left borders, but they do extend into the lower and right borders. This is expected to be flipped
078     * using {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make the other corners. If middle
079     * pieces are wanted that touch everything but the upper border, you can use
080     * {@code (LineKit.interiorSquareLarge | LineKit.flipHorizontal4x4(LineKit.interiorSquareLarge))}. If you want it to
081     * touch everything but the left border, you can use
082     * {@code (LineKit.interiorSquareLarge | LineKit.flipVertical4x4(LineKit.interiorSquareLarge))}.
083     * <br>This looks like: 
084     * <pre>
085     * "┌┬┬┬"
086     * "├┼┼┼"
087     * "├┼┼┼"
088     * "├┼┼┼"
089     * </pre>
090     * @see #interiorSquare The docs here cover how to use this as a mask with bitwise AND.
091     */
092    interiorSquareLarge = 0xFFFEFFFEFFFEDDDCL,
093    /**
094     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 square border. No lines will
095     * touch the upper or left borders, but they do extend into the lower and right borders. The entirety of this
096     * pattern is one right-angle. This is expected to be flipped using {@link #flipHorizontal4x4(long)} and/or
097     * {@link #flipVertical4x4(long)} to make the other corners.
098     * <br>This looks like: 
099     * <pre>
100     * "┌───"
101     * "│   "
102     * "│   "
103     * "│   "
104     * </pre>
105     * @see #exteriorSquare The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
106     */
107    exteriorSquareLarge = 0x000A000A000A555CL,
108    /**
109     * A constant that represents the encoded pattern for the upper left 4x4 area of a 6x6 square centered in an 8x8
110     * space. A 3x3 square will be filled of the 4x4 area this represents. No lines will touch the upper or left
111     * borders, but they do extend into the lower and right borders. This is expected to be flipped using
112     * {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners.
113     * <br>This looks like: 
114     * <pre>
115     * "    "
116     * " ┌┬┬"
117     * " ├┼┼"
118     * " ├┼┼"
119     * </pre>
120     * @see #interiorSquare The docs here cover how to use this as a mask with bitwise AND.
121     * 
122     */
123    shallowInteriorSquareLarge = 0xFFE0FFE0DDC00000L,
124    /**
125     * A constant that represents the encoded pattern for the upper left 4x4 area of a 6x6 square border centered in an
126     * 8x8 space. This consists of a 3-cell-long vertical line and a 3-cell-long horizontal line. No lines will touch
127     * the upper or left borders, but they do extend into the lower and right borders. The entirety of this
128     * pattern is one right-angle. This is expected to be flipped using {@link #flipHorizontal4x4(long)} and/or
129     * {@link #flipVertical4x4(long)} to make the other corners.
130     * <br>This looks like: 
131     * <pre>
132     * "    "
133     * " ┌──"
134     * " │  "
135     * " │  "
136     * </pre>
137     * @see #exteriorSquare The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
138     */
139    shallowExteriorSquareLarge = 0x00A000A055C00000L,
140    /**
141     * A constant that represents the encoded pattern for the upper left 4x4 area of a 4x4 square centered in an 8x8
142     * space. A 2x2 square will be filled of the 4x4 area this represents. No lines will touch the upper or left
143     * borders, but they do extend into the lower and right borders. This is expected to be flipped using
144     * {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners.
145     * <br>This looks like: 
146     * <pre>
147     * "    "
148     * "    "
149     * "  ┌┬"
150     * "  ├┼"
151     * </pre>
152     * @see #interiorSquare The docs here cover how to use this as a mask with bitwise AND.
153     */
154    shallowerInteriorSquareLarge = 0xFE00DC0000000000L,
155    /**
156     * A constant that represents the encoded pattern for the upper left 4x4 area of a 4x4 square border centered in an
157     * 8x8 space. This consists of a 2-cell-long vertical line and a 2-cell-long horizontal line. No lines will touch
158     * the upper or left borders, but they do extend into the lower and right borders. The entirety of this
159     * pattern is one right-angle. This is expected to be flipped using {@link #flipHorizontal4x4(long)} and/or
160     * {@link #flipVertical4x4(long)} to make the other corners.
161     * <br>This looks like: 
162     * <pre>
163     * "    "
164     * "    "
165     * "  ┌─"
166     * "  │ "
167     * </pre>
168     * @see #exteriorSquare The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
169     */
170    shallowerExteriorSquareLarge = 0x0A005C0000000000L,
171    /**
172     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 plus shape. No lines will
173     * touch the upper or left borders, but they do extend into the lower and right borders. This pattern leaves the
174     * upper left 2x2 area blank, and touches all of the lower and right borders. This is expected to be flipped using
175     * {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners.
176     * @see #interiorPlus The docs here cover how to use this as a mask with bitwise AND.
177     * <br>This looks like: 
178     * <pre>
179     * "  ┌┬"
180     * "  ├┼"
181     * "┌┬┼┼"
182     * "├┼┼┼"
183     * </pre>
184     */
185    interiorPlusLarge = 0xFFFEFFDCFE00DC00L,
186    /**
187     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 plus shape border. No lines
188     * will touch the upper or left borders, but they do extend into the lower and right borders. This pattern leaves
189     * the upper left 2x2 area blank, as well as all but one each of the bottom and right border cells. This is expected
190     * to be flipped using {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners.
191     * <br>This looks like: 
192     * <pre>
193     * "  ┌─"
194     * "  │ "
195     * "┌─┘ "
196     * "│   "
197     * </pre>
198     * @see #exteriorPlus The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
199     */
200    exteriorPlusLarge = 0x000A035C0A005C00L,
201    /**
202     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 circle shape. No lines will
203     * touch the upper or left borders, but they do extend into the lower and right borders. This is expected to be
204     * flipped using {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners.
205     * <br>This looks like: 
206     * <pre>
207     * "  ┌┬"
208     * " ┌┼┼"
209     * "┌┼┼┼"
210     * "├┼┼┼"
211     * </pre>
212     * @see #interiorPlus The docs here cover how to use this as a mask with bitwise AND.
213     */
214    interiorCircleLarge = 0xFFFEFFFCFFC0DC00L,
215    /**
216     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 circular border. No lines
217     * will touch the upper or left borders, but they do extend into the lower and right borders. The entirety of this
218     * pattern is one curving line. This is expected to be flipped using {@link #flipHorizontal4x4(long)} and/or
219     * {@link #flipVertical4x4(long)} to make other corners.
220     * <br>This looks like: 
221     * <pre>
222     * "  ┌─"
223     * " ┌┘ "
224     * "┌┘  "
225     * "│   "
226     * </pre>
227     * @see #exteriorPlus The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
228     */
229    exteriorCircleLarge = 0x000A003C03C05C00L,
230    /**
231     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 diamond shape. No lines will
232     * touch the upper or left borders, but they do extend into the lower and right borders. This pattern leaves the
233     * upper left 2x2 area blank, and touches all of the lower and right borders. This is expected to be flipped using
234     * {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other corners. This has more of a
235     * fine angle than {@link #interiorPlusLarge}, which is otherwise similar.
236     * <br>This looks like: 
237     * <pre>
238     * "   ┌"
239     * "  ┌┼"
240     * " ┌┼┼"
241     * "┌┼┼┼"
242     * </pre>
243     * @see #interiorPlus The docs here cover how to use this as a mask with bitwise AND.
244     */
245    interiorDiamondLarge = 0xFFFCFFC0FC00C000L,
246    /**
247     * A constant that represents the encoded pattern for the upper left 4x4 area of an 8x8 diamond shape border. No
248     * lines will touch the upper or left borders, but they do extend into the lower and right borders. This pattern
249     * leaves the upper left 2x2 area blank, as well as all but one each of the bottom and right border cells. This is
250     * expected to be flipped using {@link #flipHorizontal4x4(long)} and/or {@link #flipVertical4x4(long)} to make other
251     * corners. This has more of a fine angle than {@link #exteriorPlusLarge}, which is otherwise similar.
252     * <br>This looks like: 
253     * <pre>
254     * "   ┌"
255     * "  ┌┘"
256     * " ┌┘ "
257     * "┌┘  "
258     * </pre>
259     * @see #exteriorPlus The docs here cover how to use this as a mask with bitwise AND or to insert it with OR.
260     */
261    exteriorDiamondLarge = 0x003C03C03C00C000L;
262
263    /**
264     * Produces a 4x4 2D char array by interpreting the bits of the given long as line information. Uses the box drawing
265     * chars from {@link #light}, which are compatible with most fonts.
266     * @param encoded a long, which can be random, that encodes some pattern of (typically box drawing) characters
267     * @return a 4x4 2D char array containing elements from symbols assigned based on encoded
268     */
269    public static char[][] decode4x4(long encoded)
270    {
271        return decode4x4(encoded, light);
272    }
273    /**
274     * Produces a 4x4 2D char array by interpreting the bits of the given long as line information. Uses the given char
275     * array, which must have at least 16 elements and is usually one of {@link #light}, {@link #heavy},
276     * {@link #lightAlt}, or {@link #heavyAlt}, with the last two usable only if using a font that supports the chars
277     * {@code ╴╵╶╷} (this is true for Iosevka and Source Code Pro, for instance, but not Inconsolata or GoMono).
278     * @param encoded a long, which can be random, that encodes some pattern of (typically box drawing) characters
279     * @param symbols a 16-element-or-larger char array; usually a constant in this class like {@link #light}
280     * @return a 4x4 2D char array containing elements from symbols assigned based on encoded
281     */
282    public static char[][] decode4x4(long encoded, char[] symbols)
283    {
284        char[][] v = new char[4][4];
285        for (int i = 0; i < 16; i++) {
286            v[i & 3][i >> 2] = symbols[(int) (encoded >>> (i << 2) & 15L)];
287        }
288        return v;
289    }
290    /**
291     * Fills a 4x4 area of the given 2D char array {@code into} by interpreting the bits of the given long as line
292     * information. Uses the given char array {@code symbols}, which must have at least 16 elements and is usually one
293     * of {@link #light}, {@link #heavy}, {@link #lightAlt}, or {@link #heavyAlt}, with the last two usable only if
294     * using a font that supports the chars {@code ╴╵╶╷} (this is true for Iosevka and Source Code Pro, for instance,
295     * but not Inconsolata or GoMono).
296     * @param encoded a long, which can be random, that encodes some pattern of (typically box drawing) characters
297     * @param symbols a 16-element-or-larger char array; usually a constant in this class like {@link #light}
298     * @param into a 2D char array that will be modified in a 4x4 area
299     * @param startX the first x position to modify in into
300     * @param startY the first y position to modify in into
301     * @return into, after modification
302     */
303    public static char[][] decodeInto4x4(long encoded, char[] symbols, char[][] into, int startX, int startY)
304    {
305        for (int i = 0; i < 16; i++) {
306            into[(i & 3) + startX][(i >> 2) + startY] = symbols[(int) (encoded >>> (i << 2) & 15L)];
307        }
308        return into;
309    }
310
311    /**
312     * Reads a 2D char array {@code decoded}, which must be at least 4x4 in size, and returns a long that encodes the cells from 0,0 to
313     * 3,3 in a way that this class can interpret and manipulate. The 2D array {@code decoded} must contain box drawing
314     * symbols, which can be any of those from {@link #light}, {@link #heavy}, {@link #lightAlt}, or {@link #heavyAlt}.
315     * Valid chars are {@code ╴╵┘╶─└┴╷┐│┤┌┬├┼╸╹┛╺━┗┻╻┓┃┫┏┳┣╋}; any other chars will be treated as empty space.
316     * @param decoded a 2D char array that must be at least 4x4 and should usually contain box drawing characters
317     * @return a long that encodes the box drawing information in decoded so this class can manipulate it
318     */
319    public static long encode4x4(char[][] decoded)
320    {
321        long v = 0L;
322        for (int i = 0; i < 16; i++) {
323            switch (decoded[i & 3][i >> 2])
324            {
325                // ╴╵┘╶─└┴╷┐│┤┌┬├┼
326                // ╸╹┛╺━┗┻╻┓┃┫┏┳┣╋
327                //0123456789ABCDEF
328                case '─':
329                case '━':
330                    v |= 5L << (i << 2);
331                    break;
332                case '│':
333                case '┃':
334                    v |= 10L << (i << 2);
335                    break;
336                case '┘':
337                case '┛':
338                    v |= 3L << (i << 2);
339                    break;
340                case '└':
341                case '┗':
342                    v |= 6L << (i << 2);
343                    break;
344                case '┐':
345                case '┓':
346                    v |= 9L << (i << 2);
347                    break;
348                case '┌':
349                case '┏':
350                    v |= 12L << (i << 2);
351                    break;
352                case '┴':
353                case '┻':
354                    v |= 7L << (i << 2);
355                    break;
356                case '┤':
357                case '┫':
358                    v |= 11L << (i << 2);
359                    break;
360                case '┬':
361                case '┳':
362                    v |= 13L << (i << 2);
363                    break;
364                case '├':
365                case '┣':
366                    v |= 14L << (i << 2);
367                    break;
368                case '┼':
369                case '╋':
370                    v |= 15L << (i << 2);
371                    break;
372                case '╴':
373                case '╸':
374                    v |= 1L << (i << 2);
375                    break;
376                case '╵':
377                case '╹':
378                    v |= 2L << (i << 2);
379                    break;
380                case '╶':
381                case '╺':
382                    v |= 4L << (i << 2);
383                    break;
384                case '╷':
385                case '╻':
386                    v |= 8L << (i << 2);
387                    break;
388            }
389        }
390        return v;
391    }
392
393    /**
394     * Makes a variant on the given encoded 4x4 pattern so the left side is flipped to the right side and vice versa.
395     * @param encoded an encoded pattern long that represents a 4x4 area
396     * @return a different encoded pattern long that represents the argument flipped left-to-right
397     */
398    public static long flipHorizontal4x4(long encoded)
399    {
400        long v = 0L;
401        for (int i = 0, i4 = 0; i < 16; i++, i4 += 4) {
402            v |= ((encoded >>> i4 & 10L) | ((encoded >>> i4 & 1) << 2L) | ((encoded >>> i4 & 4L) >>> 2)) << (i + 3 - ((i & 3) << 1) << 2);
403        }
404        return v;
405    }
406    /**
407     * Makes a variant on the given encoded 4x4 pattern so the top side is flipped to the bottom side and vice versa.
408     * @param encoded an encoded pattern long that represents a 4x4 area
409     * @return a different encoded pattern long that represents the argument flipped top-to-bottom
410     */
411    public static long flipVertical4x4(long encoded)
412    {
413        long v = 0L;
414        for (int i = 0, i4 = 0; i < 16; i++, i4 += 4) {
415            v |= ((encoded >>> i4 & 5L) | ((encoded >>> i4 & 2L) << 2) | ((encoded >>> i4 & 8L) >>> 2)) << (i + 12 - ((i >> 2) << 3) << 2);
416        }
417        return v;
418    }
419
420    /**
421     * Makes a variant on the given encoded 4x4 pattern so the x and y axes are interchanged, making the top side become
422     * the left side and vice versa, while the bottom side becomes the right side and vice versa.
423     * @param encoded an encoded pattern long that represents a 4x4 area
424     * @return a different encoded pattern long that represents the argument transposed top-to-left and bottom-to-right
425     */
426    public static long transpose4x4(long encoded)
427    {
428        long v = 0L;
429        for (int i4 = 0; i4 < 64; i4 += 4) {
430            v |= (((encoded >>> i4 & 5L) << 1) | ((encoded >>> i4 & 10L) >>> 1)) << ((i4 >>> 2 & 12L) | ((i4 & 12L) << 2));
431        }
432        return v;
433    }
434    /**
435     * Makes a variant on the given encoded 4x4 pattern so the lines are rotated 90 degrees clockwise, changing their
436     * positions as well as what chars they will decode to. This can be called twice to get a 180 degree rotation, but
437     * {@link #rotateCounterclockwise(long)} should be used for a 270 degree rotation.
438     * @param encoded an encoded pattern long that represents a 4x4 area
439     * @return a different encoded pattern long that represents the argument rotated 90 degrees clockwise
440     */
441    public static long rotateClockwise(long encoded)
442    {
443        // this is functionally equivalent to, but faster than, the following:
444        // return flipHorizontal4x4(transpose4x4(encoded));
445        long v = 0L;
446        for (int i4 = 0; i4 < 64; i4 += 4) {
447            v |= (((encoded >>> i4 & 7L) << 1) | ((encoded >>> i4 & 8L) >>> 3)) << ((~i4 >>> 2 & 12L) | ((i4 & 12L) << 2));
448        }
449        return v;
450    }
451    /**
452     * Makes a variant on the given encoded 4x4 pattern so the lines are rotated 90 degrees counterclockwise, changing
453     * their positions as well as what chars they will decode to. This can be called twice to get a 180 degree rotation,
454     * but {@link #rotateClockwise(long)} should be used for a 270 degree rotation.
455     * @param encoded an encoded pattern long that represents a 4x4 area
456     * @return a different encoded pattern long that represents the argument rotated 90 degrees counterclockwise
457     */
458    public static long rotateCounterclockwise(long encoded)
459    {
460        // this is functionally equivalent to, but faster than, the following:
461        // return flipVertical4x4(transpose4x4(encoded));
462        long v = 0L;
463        for (int i4 = 0; i4 < 64; i4 += 4) {
464            v |= ((encoded >>> (i4 + 1) & 7L) | ((encoded >>> i4 & 1L) << 3)) << ((i4 >>> 2 & 12L) | ((~i4 & 12L) << 2));
465        }
466        return v;
467    }
468
469    /** Adjusts an existing map that uses box-drawing characters so non-visible line segments aren't rendered.
470     * Takes a map that was produced using {@link DungeonUtility#hashesToLines(char[][])} and a GreasedRegion that
471     * stores already-seen cells, and writes an altered version of the 2D char array {@code map} to {@code writeInto},
472     * leaving non-box-drawing chars unchanged. This method modifies writeInto in-place, and also returns it after those
473     * changesare made. The way this works is explained well with an example: if the player is north of a T-junction
474     * wall, '┬', then unless he has already explored the area south of his position, the bottom segment of the wall
475     * isn't visible to him, and so '─' should be rendered instead of '┬'. If a cell has already been seen, it is
476     * considered still visible for the purpose of calculating shown segments (it won't change once you leave an area).
477     * @param map a 2D char array that should have been produced by {@link DungeonUtility#hashesToLines(char[][])}
478     * @param seen a GreasedRegion where "on" cells are visible now or were visible in the past
479     * @param writeInto a 2D char array that must have at least the dimensions of map; will be modified
480     * @return writeInto, after modifications
481     */
482    public static char[][] pruneLines(char[][] map, GreasedRegion seen, char[][] writeInto)
483    {
484        return pruneLines(map, seen, light, writeInto);
485    }
486
487    /** Adjusts an existing map that uses box-drawing characters so non-visible line segments aren't rendered.
488     * Takes a map that was produced using {@link DungeonUtility#hashesToLines(char[][])} a GreasedRegion that stores
489     * already-seen cells, an optional char array that refers to a line drawing style constant in this class (defaults
490     * to {@link #light}, and writes an altered version of the 2D char array {@code map} to {@code writeInto}, leaving
491     * non-box-drawing chars unchanged. This method modifies writeInto in-place, and also returns it after those changes
492     * are made. The way this works is explained well with an example: if the player is north of a T-junction wall, '┬',
493     * then unless he has already explored the area south of his position, the bottom segment of the wall isn't visible
494     * to him, and so '─' should be rendered instead of '┬'. If a cell has already been seen, it is considered still
495     * visible for the purpose of calculating shown segments (it won't change once you leave an area).
496     * @param map a 2D char array that should have been produced by {@link DungeonUtility#hashesToLines(char[][])}
497     * @param seen a GreasedRegion where "on" cells are visible now or were visible in the past
498     * @param symbols a char array that should be {@link #light} or {@link #heavy} unless you know your font supports
499     *                the chars "╴╵╶╷", in which case you can use {@link #lightAlt}, or the heavy-weight versions of 
500     *                those chars, in which case you can use {@link #heavyAlt}
501     * @param writeInto a 2D char array that must have at least the dimensions of map; will be modified
502     * @return writeInto, after modifications
503     */
504    public static char[][] pruneLines(char[][] map, GreasedRegion seen, char[] symbols, char[][] writeInto)
505    {
506        final int width = map.length, height = map[0].length;
507        int mask;
508        for (int x = 0; x < width; x++) {
509            for (int y = 0; y < height; y++) {
510                if(seen.contains(x, y)) 
511                {
512                    mask = 15;
513                    if(!seen.contains(x-1, y))
514                        mask ^= 1;
515                    if(!seen.contains(x+1, y))
516                        mask ^= 4;
517                    if(!seen.contains(x, y-1))
518                        mask ^= 2;
519                    if(!seen.contains(x, y+1))
520                        mask ^= 8;
521                    switch (map[x][y]) {
522                        case '─':
523                        case '━':
524                            writeInto[x][y] = symbols[5 & mask];
525                            break;
526                        case '│':
527                        case '┃':
528                            writeInto[x][y] = symbols[10 & mask];
529                            break;
530                        case '┘':
531                        case '┛':
532                            writeInto[x][y] = symbols[3 & mask];
533                            break;
534                        case '└':
535                        case '┗':
536                            writeInto[x][y] = symbols[6 & mask];
537                            break;
538                        case '┐':
539                        case '┓':
540                            writeInto[x][y] = symbols[9 & mask];
541                            break;
542                        case '┌':
543                        case '┏':
544                            writeInto[x][y] = symbols[12 & mask];
545                            break;
546                        case '┴':
547                        case '┻':
548                            writeInto[x][y] = symbols[7 & mask];
549                            break;
550                        case '┤':
551                        case '┫':
552                            writeInto[x][y] = symbols[11 & mask];
553                            break;
554                        case '┬':
555                        case '┳':
556                            writeInto[x][y] = symbols[13 & mask];
557                            break;
558                        case '├':
559                        case '┣':
560                            writeInto[x][y] = symbols[14 & mask];
561                            break;
562                        case '┼':
563                        case '╋':
564                            writeInto[x][y] = symbols[15 & mask];
565                            break;
566                        case '╴':
567                        case '╸':
568                            writeInto[x][y] = symbols[1 & mask];
569                            break;
570                        case '╵':
571                        case '╹':
572                            writeInto[x][y] = symbols[2 & mask];
573                            break;
574                        case '╶':
575                        case '╺':
576                            writeInto[x][y] = symbols[4 & mask];
577                            break;
578                        case '╷':
579                        case '╻':
580                            writeInto[x][y] = symbols[8 & mask];
581                            break;
582                    }
583                }
584            }
585        }
586        return writeInto;
587    }
588
589//    public static void main(String[] args)
590//    {
591//        System.out.printf("interiorSquare = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
592//                {
593//                        {'┌', '┬', '┬', '┐'},
594//                        {'├', '┼', '┼', '┤'},
595//                        {'├', '┼', '┼', '┤'},
596//                        {'└', '┴', '┴', '┘'},
597//                })));
598//        System.out.printf("exteriorSquare = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
599//                {
600//                        {'┌', '─', '─', '┐'},
601//                        {'│', ' ', ' ', '│'},
602//                        {'│', ' ', ' ', '│'},
603//                        {'└', '─', '─', '┘'},
604//                })));
605//        System.out.printf("interiorPlus = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
606//                {
607//                        {' ', '┌', '┐', ' '},
608//                        {'┌', '┼', '┼', '┐'},
609//                        {'└', '┼', '┼', '┘'},
610//                        {' ', '└', '┘', ' '},
611//                })));
612//        System.out.printf("exteriorPlus = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
613//                {
614//                        {' ', '┌', '┐', ' '},
615//                        {'┌', '┘', '└', '┐'},
616//                        {'└', '┐', '┌', '┘'},
617//                        {' ', '└', '┘', ' '},
618//                })));
619//        System.out.printf("interiorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
620//                {
621//                        {'┌', '┬', '┬', '┬'},
622//                        {'├', '┼', '┼', '┼'},
623//                        {'├', '┼', '┼', '┼'},
624//                        {'├', '┼', '┼', '┼'},
625//                })));
626//        System.out.printf("exteriorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
627//                {
628//                        {'┌', '─', '─', '─'},
629//                        {'│', ' ', ' ', ' '},
630//                        {'│', ' ', ' ', ' '},
631//                        {'│', ' ', ' ', ' '},
632//                })));
633//        System.out.printf("interiorPlusLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
634//                {
635//                        {' ', ' ', '┌', '┬'},
636//                        {' ', ' ', '├', '┼'},
637//                        {'┌', '┬', '┼', '┼'},
638//                        {'├', '┼', '┼', '┼'},
639//                })));
640//        System.out.printf("exteriorPlusLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
641//                {
642//                        {' ', ' ', '┌', '─'},
643//                        {' ', ' ', '│', ' '},
644//                        {'┌', '─', '┘', ' '},
645//                        {'│', ' ', ' ', ' '},
646//                })));
647//        System.out.printf("interiorCircleLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
648//                {
649//                        {' ', ' ', '┌', '┬'},
650//                        {' ', '┌', '┼', '┼'},
651//                        {'┌', '┼', '┼', '┼'},
652//                        {'├', '┼', '┼', '┼'},
653//                })));
654//        System.out.printf("exteriorCircleLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
655//                {
656//                        {' ', ' ', '┌', '─'},
657//                        {' ', '┌', '┘', ' '},
658//                        {'┌', '┘', ' ', ' '},
659//                        {'│', ' ', ' ', ' '},
660//                })));
661//        System.out.printf("interiorDiamondLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
662//                {
663//                        {' ', ' ', ' ', '┌'},
664//                        {' ', ' ', '┌', '┼'},
665//                        {' ', '┌', '┼', '┼'},
666//                        {'┌', '┼', '┼', '┼'},
667//                })));
668//        System.out.printf("exteriorDiamondLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
669//                {
670//                        {' ', ' ', ' ', '┌'},
671//                        {' ', ' ', '┌', '┘'},
672//                        {' ', '┌', '┘', ' '},
673//                        {'┌', '┘', ' ', ' '},
674//                })));
675//        System.out.printf("shallowInteriorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
676//                {
677//                        {' ', ' ', ' ', ' '},
678//                        {' ', '┌', '┬', '┬'},
679//                        {' ', '├', '┼', '┼'},
680//                        {' ', '├', '┼', '┼'},
681//                })));
682//        System.out.printf("shallowExteriorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
683//                {
684//                        {' ', ' ', ' ', ' '},
685//                        {' ', '┌', '─', '─'},
686//                        {' ', '│', ' ', ' '},
687//                        {' ', '│', ' ', ' '},
688//                })));
689//        System.out.printf("shallowerInteriorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
690//                {
691//                        {' ', ' ', ' ', ' '},
692//                        {' ', ' ', ' ', ' '},
693//                        {' ', ' ', '┌', '┬'},
694//                        {' ', ' ', '├', '┼'},
695//                })));
696//        System.out.printf("shallowerExteriorSquareLarge = 0x%016XL\n", LineKit.encode4x4(DungeonUtility.transposeLines(new char[][]
697//                {
698//                        {' ', ' ', ' ', ' '},
699//                        {' ', ' ', ' ', ' '},
700//                        {' ', ' ', '┌', '─'},
701//                        {' ', ' ', '│', ' '},
702//                })));
703//    }
704}