├── .travis.yml ├── Dockerfile ├── dist └── drawille-1.0.2.jar ├── docs ├── assets │ ├── example_1.png │ └── example_2.png └── examples │ ├── CanvasDemo.java │ └── TurtleDemo.java ├── .gitignore ├── src ├── test │ └── java │ │ └── io │ │ └── raffi │ │ └── drawille │ │ ├── DrawilleExceptionTest.java │ │ ├── TurtleTest.java │ │ ├── BrailleMapTest.java │ │ └── CanvasTest.java └── main │ └── java │ └── io │ └── raffi │ └── drawille │ ├── DrawilleException.java │ ├── BrailleMap.java │ ├── Turtle.java │ └── Canvas.java ├── LICENSE.md ├── pom.xml └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: mvn test -B 3 | jdk: 4 | - oraclejdk8 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:latest 2 | 3 | VOLUME /usr/src/drawille 4 | WORKDIR /usr/src/drawille -------------------------------------------------------------------------------- /dist/drawille-1.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/null93/drawille/HEAD/dist/drawille-1.0.2.jar -------------------------------------------------------------------------------- /docs/assets/example_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/null93/drawille/HEAD/docs/assets/example_1.png -------------------------------------------------------------------------------- /docs/assets/example_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/null93/drawille/HEAD/docs/assets/example_2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | /target/ 3 | /private/ 4 | 5 | # Files 6 | .DS_Store 7 | *.class 8 | *~ 9 | -------------------------------------------------------------------------------- /docs/examples/CanvasDemo.java: -------------------------------------------------------------------------------- 1 | package docs.examples; 2 | 3 | import io.raffi.drawille.Canvas; 4 | 5 | public class CanvasDemo { 6 | 7 | public static void main (String[] args) { 8 | Canvas canvas = new Canvas ( 75, 6 ); 9 | for ( int x = 0; x <= canvas.getWidth () * 8; x++ ) { 10 | canvas.set ( x / 10, ( int ) Math.round ( 10 + Math.cos ( x * Math.PI / 180 ) * 10 ) ); 11 | } 12 | canvas.render (); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /docs/examples/TurtleDemo.java: -------------------------------------------------------------------------------- 1 | package docs.examples; 2 | 3 | import io.raffi.drawille.Turtle; 4 | 5 | public class TurtleDemo { 6 | 7 | public static void main (String[] args) { 8 | Turtle turtle = new Turtle ( 75, 50 ); 9 | turtle.move ( turtle.getWidth () / 2, turtle.getHeight () / 2 ); 10 | turtle.down (); 11 | for ( int x = 0; x < 72; x++ ) { 12 | turtle.right ( 20 ); 13 | for ( int y = 0; y < 72; y++ ) { 14 | turtle.right ( 20 ); 15 | turtle.forward ( 10 ); 16 | } 17 | } 18 | turtle.render (); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/test/java/io/raffi/drawille/DrawilleExceptionTest.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | public class DrawilleExceptionTest extends TestCase { 8 | 9 | public void testExceptionThrown () { 10 | Boolean thrown = false; 11 | try { 12 | throw new DrawilleException ( -1, -1 ); 13 | } 14 | catch ( DrawilleException e ) { 15 | thrown = true; 16 | } 17 | catch ( Exception e ) { 18 | thrown = false; 19 | } 20 | assertTrue ( thrown ); 21 | } 22 | 23 | public void testExceptionPassParameters () { 24 | String message = ""; 25 | try { 26 | throw new DrawilleException ( -1, -2 ); 27 | } 28 | catch ( DrawilleException e ) { 29 | message = e.getMessage (); 30 | } 31 | assertTrue ( message.equals ("Out of range {x:-1,y:-2}") ); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | Copyright 2018 Rafael Grigorian 3 | 4 | * * * 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/io/raffi/drawille/DrawilleException.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | /** 4 | * This class inherits from the RuntimeException class. It is meant to be thrown whenever an out of 5 | * range value is passed to the Canvas and BrailleMap class. The message is statically defined in 6 | * this class and the caller only has to pass in the out of bounds (x,y) value pairs. 7 | * @version 1.0.3 8 | * @package io.raffi.drawille 9 | * @author Rafael Grigorian 10 | * @copyright 2018 Rafael Grigorian — All Rights Reserved 11 | * @license MIT License 12 | */ 13 | public class DrawilleException extends RuntimeException { 14 | 15 | /** 16 | * This constructor takes in an (x,y) value pair and displays those pairs to the user. These 17 | * values are defined to be out of range by the caller, therefore the caller will be alerted. 18 | * @param Integer x The passed horizontal coordinate 19 | * @param Integer y The passed vertical coordinate 20 | */ 21 | public DrawilleException ( int x, int y ) { 22 | super ( String.format ( "Out of range {x:%d,y:%d}", x, y ) ); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/test/java/io/raffi/drawille/TurtleTest.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | public class TurtleTest extends TestCase { 8 | 9 | public void testGettersWork () { 10 | Turtle turtle = new Turtle ( 1, 1 ); 11 | assertTrue ( turtle.getX () == 0 ); 12 | assertTrue ( turtle.getY () == 0 ); 13 | turtle.move ( 1, 2 ); 14 | assertTrue ( turtle.getX () == 1 ); 15 | assertTrue ( turtle.getY () == 2 ); 16 | } 17 | 18 | public void testAngleWorks () { 19 | Turtle turtle = new Turtle ( 1, 1 ); 20 | assertTrue ( turtle.getAngle () == 0 ); 21 | turtle.left ( 45 ); 22 | assertTrue ( turtle.getAngle () == -45 ); 23 | turtle.right ( 90 ); 24 | assertTrue ( turtle.getAngle () == 45 ); 25 | } 26 | 27 | public void testPenIsDrawing () { 28 | Turtle turtle = new Turtle ( 1, 1 ); 29 | assertFalse ( turtle.get ( 0, 0 ) ); 30 | assertFalse ( turtle.get ( 0, 1 ) ); 31 | assertFalse ( turtle.get ( 0, 2 ) ); 32 | assertFalse ( turtle.get ( 0, 3 ) ); 33 | turtle.move ( 0, 3 ); 34 | assertFalse ( turtle.get ( 0, 0 ) ); 35 | assertFalse ( turtle.get ( 0, 1 ) ); 36 | assertFalse ( turtle.get ( 0, 2 ) ); 37 | assertFalse ( turtle.get ( 0, 3 ) ); 38 | turtle.down (); 39 | turtle.move ( 0, 0 ); 40 | assertTrue ( turtle.get ( 0, 0 ) ); 41 | assertTrue ( turtle.get ( 0, 1 ) ); 42 | assertTrue ( turtle.get ( 0, 2 ) ); 43 | assertTrue ( turtle.get ( 0, 3 ) ); 44 | turtle.up (); 45 | turtle.move ( 1, 0 ); 46 | assertFalse ( turtle.get ( 1, 0 ) ); 47 | assertTrue ( turtle.get ( 0, 0 ) ); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | io.raffi 7 | drawille 8 | jar 9 | 1.0.3 10 | drawille 11 | http://maven.apache.org 12 | 13 | UTF-8 14 | 15 | 16 | 17 | junit 18 | junit 19 | 3.8.1 20 | test 21 | 22 | 23 | 24 | 25 | 26 | com.google.code.maven-replacer-plugin 27 | replacer 28 | 1.5.2 29 | 30 | 31 | prepare-package 32 | 33 | replace 34 | 35 | 36 | 37 | 38 | 39 | src/**/*.java 40 | 41 | 42 | 43 | (\*\s+@version\s+)([0-9\.]+) 44 | $1${project.version} 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-compiler-plugin 52 | 3.6.1 53 | 54 | 1.8 55 | 1.8 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-surefire-plugin 61 | 2.15 62 | 63 | -Dfile.encoding=UTF-8 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/test/java/io/raffi/drawille/BrailleMapTest.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | public class BrailleMapTest extends TestCase { 8 | 9 | private Boolean throwsException ( int x, int y ) { 10 | BrailleMap map = new BrailleMap (); 11 | Boolean thrown = false; 12 | try { 13 | map.set ( x, y ); 14 | } 15 | catch ( DrawilleException e ) { 16 | thrown = true; 17 | } 18 | catch ( Exception e ) { 19 | thrown = false; 20 | } 21 | return thrown; 22 | } 23 | 24 | public void testExceptionIsThrown () { 25 | assertTrue ( this.throwsException ( -1, 0 ) ); 26 | assertTrue ( this.throwsException ( 0, -1 ) ); 27 | assertTrue ( this.throwsException ( 2, 0 ) ); 28 | assertTrue ( this.throwsException ( 1, 4 ) ); 29 | assertFalse ( this.throwsException ( 0, 0 ) ); 30 | assertFalse ( this.throwsException ( 1, 2 ) ); 31 | } 32 | 33 | public void testChangeWorks () { 34 | BrailleMap map = new BrailleMap (); 35 | assertFalse ( map.get ( 0, 0 ) ); 36 | map.change ( 0, 0, true ); 37 | assertTrue ( map.get ( 0, 0 ) ); 38 | map.change ( 0, 0, false ); 39 | assertFalse ( map.get ( 0, 0 ) ); 40 | } 41 | 42 | public void testGetWorks () { 43 | BrailleMap map = new BrailleMap (); 44 | assertFalse ( map.get ( 0, 0 ) ); 45 | map.change ( 0, 0, true ); 46 | assertTrue ( map.get ( 0, 0 ) ); 47 | } 48 | 49 | public void testSetWorks () { 50 | BrailleMap map = new BrailleMap (); 51 | assertFalse ( map.get ( 0, 0 ) ); 52 | map.set ( 0, 0 ); 53 | assertTrue ( map.get ( 0, 0 ) ); 54 | assertFalse ( map.get ( 1, 3 ) ); 55 | map.set ( 1, 3 ); 56 | assertTrue ( map.get ( 1, 3 ) ); 57 | } 58 | 59 | public void testUnsetWorks () { 60 | BrailleMap map = new BrailleMap (); 61 | assertFalse ( map.get ( 0, 0 ) ); 62 | map.set ( 0, 0 ); 63 | assertTrue ( map.get ( 0, 0 ) ); 64 | map.unset ( 0, 0 ); 65 | assertFalse ( map.get ( 0, 0 ) ); 66 | } 67 | 68 | public void testToggleWorks () { 69 | BrailleMap map = new BrailleMap (); 70 | assertFalse ( map.get ( 0, 0 ) ); 71 | map.toggle ( 0, 0 ); 72 | assertTrue ( map.get ( 0, 0 ) ); 73 | map.toggle ( 0, 0 ); 74 | assertFalse ( map.get ( 0, 0 ) ); 75 | } 76 | 77 | public void testResetWorks () { 78 | BrailleMap map = new BrailleMap (); 79 | map.toggle ( 0, 0 ); 80 | map.toggle ( 0, 1 ); 81 | map.toggle ( 1, 1 ); 82 | map.reset (); 83 | assertFalse ( map.get ( 0, 0 ) ); 84 | assertFalse ( map.get ( 0, 1 ) ); 85 | assertFalse ( map.get ( 1, 1 ) ); 86 | } 87 | 88 | public void testToStringWorks () { 89 | BrailleMap map = new BrailleMap (); 90 | assertTrue ( map.toString ().equals ("⠀") ); 91 | map.change ( 0, 0, true ); 92 | assertTrue ( map.toString ().equals ("⠁") ); 93 | map.change ( 0, 0, false ); 94 | assertTrue ( map.toString ().equals ("⠀") ); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drawille 2 | > Pixel graphics in terminal implemented with unicode braille characters 3 | 4 | ![MIT License](https://img.shields.io/badge/License-MIT-lightgrey.svg?style=for-the-badge) 5 | ![Version 1.0.3](https://img.shields.io/badge/Version-1.0.3-lightgrey.svg?style=for-the-badge) 6 | ![Travis CI](https://img.shields.io/travis/null93/drawille.svg?style=for-the-badge&colorB=9f9f9f) 7 | 8 | ## About 9 | 10 | This project is a Java port of the original [drawille](https://github.com/asciimoo/drawille) python project by [asciimoo](https://github.com/asciimoo). This project serves as a library for Java to draw in the console using braille letters that are part of the unicode character space. Braille characters in unicode have a 4 by 2 matrix that can be used as a sub-matrix for each character in a console screen. This braille dot matrix effectively raises the resolution of any console by eight times. The examples below were rendered in the console using this Java library. The original ideas for these examples came from the original project repository. 11 | 12 | ## Examples 13 | 14 | ```java 15 | Canvas canvas = new Canvas ( 75, 6 ); 16 | for ( int x = 0; x <= canvas.getWidth () * 8; x++ ) { 17 | canvas.set ( x / 10, ( int ) Math.round ( 10 + Math.cos ( x * Math.PI / 180 ) * 10 ) ); 18 | } 19 | canvas.render (); 20 | ``` 21 | 22 | ![Example #01](docs/assets/example_1.png) 23 | 24 | ```java 25 | Turtle turtle = new Turtle ( 75, 50 ); 26 | turtle.move ( turtle.getWidth () / 2, turtle.getHeight () / 2 ); 27 | turtle.down (); 28 | for ( int x = 0; x < 72; x++ ) { 29 | turtle.right ( 20 ); 30 | for ( int y = 0; y < 72; y++ ) { 31 | turtle.right ( 20 ); 32 | turtle.forward ( 10 ); 33 | } 34 | } 35 | turtle.render (); 36 | ``` 37 | 38 | ![Example #02](docs/assets/example_2.png) 39 | 40 | ## Building & Running 41 | 42 | This project uses maven as a build system. Therefore to package this library into a jar, execute `mvn package` while in the project root directory. Since braille characters are part of the unicode domain, it is important to append the `-Dfile.encoding=UTF-8` flag when running your Java application. This will ensure that the braille characters are rendered correctly in your console. If this flag is not passed, then you will likely see the `?` character in place of it. 43 | 44 | ## Development Environment 45 | 46 | Docker can be used to spin up a quick development environment: 47 | 48 | ```bash 49 | docker build -t drawille . 50 | docker run -it -v `pwd`:/usr/src/drawille drawille bash 51 | ``` 52 | 53 | Once inside the container, you can compile and run the examples: 54 | 55 | ```bash 56 | mvn package 57 | javac -cp target/drawille-1.0.3.jar docs/examples/*Demo.java 58 | java -cp .:target/drawille-1.0.3.jar docs/examples/CanvasDemo 59 | java -cp .:target/drawille-1.0.3.jar docs/examples/TurtleDemo 60 | ``` 61 | 62 | ## Bugs / Feature Requests 63 | If you have any feature requests, please open up an issue. Similarly if there are any bugs found, please report them by opening up an issue. If a bug is found, please include steps to reproduce the issue, alongside the expected and actual behavior. 64 | -------------------------------------------------------------------------------- /src/test/java/io/raffi/drawille/CanvasTest.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import junit.framework.Test; 5 | import junit.framework.TestCase; 6 | import junit.framework.TestSuite; 7 | 8 | public class CanvasTest extends TestCase { 9 | 10 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream (); 11 | private final ByteArrayOutputStream errContent = new ByteArrayOutputStream (); 12 | 13 | private Boolean throwsException ( int x, int y ) { 14 | Canvas canvas = new Canvas ( 1, 1 ); 15 | Boolean thrown = false; 16 | try { 17 | canvas.set ( x, y ); 18 | } 19 | catch ( DrawilleException e ) { 20 | thrown = true; 21 | } 22 | catch ( Exception e ) { 23 | thrown = false; 24 | } 25 | return thrown; 26 | } 27 | 28 | public void testExceptionIsThrown () { 29 | assertTrue ( this.throwsException ( -1, 0 ) ); 30 | assertTrue ( this.throwsException ( 0, -1 ) ); 31 | assertTrue ( this.throwsException ( 2, 0 ) ); 32 | assertTrue ( this.throwsException ( 1, 4 ) ); 33 | assertFalse ( this.throwsException ( 0, 0 ) ); 34 | assertFalse ( this.throwsException ( 1, 2 ) ); 35 | } 36 | 37 | public void testDimensionGetterWorks () { 38 | Canvas c1 = new Canvas ( 10, 30 ); 39 | Canvas c2 = new Canvas ( 0, 0 ); 40 | Canvas c3 = new Canvas ( 2, 5 ); 41 | assertTrue ( c1.getWidth () == 10 * 2 ); 42 | assertTrue ( c1.getHeight () == 30 * 4 ); 43 | assertTrue ( c2.getWidth () == 0 * 2 ); 44 | assertTrue ( c2.getHeight () == 0 * 4 ); 45 | assertTrue ( c3.getWidth () == 2 * 2 ); 46 | assertTrue ( c3.getHeight () == 5 * 4 ); 47 | } 48 | 49 | public void testGetterWorks () { 50 | Canvas canvas = new Canvas ( 1, 1 ); 51 | assertFalse ( canvas.get ( 0, 0 ) ); 52 | assertFalse ( canvas.get ( 1, 1 ) ); 53 | canvas.set ( 0, 0 ); 54 | canvas.set ( 1, 1 ); 55 | assertTrue ( canvas.get ( 0, 0 ) ); 56 | assertTrue ( canvas.get ( 1, 1 ) ); 57 | } 58 | 59 | public void testChangeWorks () { 60 | Canvas canvas = new Canvas ( 1, 1 ); 61 | assertFalse ( canvas.get ( 0, 0 ) ); 62 | assertFalse ( canvas.get ( 1, 1 ) ); 63 | canvas.change ( 0, 0, true ); 64 | canvas.change ( 1, 1, true ); 65 | assertTrue ( canvas.get ( 0, 0 ) ); 66 | assertTrue ( canvas.get ( 1, 1 ) ); 67 | canvas.change ( 0, 0, false ); 68 | canvas.change ( 1, 1, false ); 69 | assertFalse ( canvas.get ( 0, 0 ) ); 70 | assertFalse ( canvas.get ( 1, 1 ) ); 71 | } 72 | 73 | public void testSetWorks () { 74 | Canvas canvas = new Canvas ( 1, 1 ); 75 | assertFalse ( canvas.get ( 0, 0 ) ); 76 | assertFalse ( canvas.get ( 1, 1 ) ); 77 | canvas.set ( 0, 0 ); 78 | canvas.set ( 1, 1 ); 79 | assertTrue ( canvas.get ( 0, 0 ) ); 80 | assertTrue ( canvas.get ( 1, 1 ) ); 81 | } 82 | 83 | public void testUnsetWorks () { 84 | Canvas canvas = new Canvas ( 1, 1 ); 85 | assertFalse ( canvas.get ( 0, 0 ) ); 86 | assertFalse ( canvas.get ( 1, 1 ) ); 87 | canvas.set ( 0, 0 ); 88 | canvas.set ( 1, 1 ); 89 | assertTrue ( canvas.get ( 0, 0 ) ); 90 | assertTrue ( canvas.get ( 1, 1 ) ); 91 | canvas.unset ( 0, 0 ); 92 | canvas.unset ( 1, 1 ); 93 | assertFalse ( canvas.get ( 0, 0 ) ); 94 | assertFalse ( canvas.get ( 1, 1 ) ); 95 | } 96 | 97 | public void testToggleWorks () { 98 | Canvas canvas = new Canvas ( 1, 1 ); 99 | assertFalse ( canvas.get ( 0, 0 ) ); 100 | assertFalse ( canvas.get ( 1, 1 ) ); 101 | canvas.toggle ( 0, 0 ); 102 | canvas.toggle ( 1, 1 ); 103 | assertTrue ( canvas.get ( 0, 0 ) ); 104 | assertTrue ( canvas.get ( 1, 1 ) ); 105 | canvas.toggle ( 0, 0 ); 106 | canvas.toggle ( 1, 1 ); 107 | assertFalse ( canvas.get ( 0, 0 ) ); 108 | assertFalse ( canvas.get ( 1, 1 ) ); 109 | } 110 | 111 | public void testClearWorks () { 112 | Canvas canvas = new Canvas ( 1, 1 ); 113 | assertFalse ( canvas.get ( 0, 0 ) ); 114 | assertFalse ( canvas.get ( 1, 1 ) ); 115 | assertFalse ( canvas.get ( 1, 2 ) ); 116 | canvas.toggle ( 0, 0 ); 117 | canvas.toggle ( 1, 1 ); 118 | canvas.toggle ( 1, 2 ); 119 | assertTrue ( canvas.get ( 0, 0 ) ); 120 | assertTrue ( canvas.get ( 1, 1 ) ); 121 | assertTrue ( canvas.get ( 1, 2 ) ); 122 | canvas.clear (); 123 | assertFalse ( canvas.get ( 0, 0 ) ); 124 | assertFalse ( canvas.get ( 1, 1 ) ); 125 | assertFalse ( canvas.get ( 1, 2 ) ); 126 | } 127 | 128 | public void testRenderOverload () { 129 | try { 130 | ByteArrayOutputStream output = new ByteArrayOutputStream (); 131 | Canvas canvas = new Canvas ( 1, 2 ); 132 | canvas.set ( 1, 1 ); 133 | canvas.set ( 1, 2 ); 134 | String result = canvas.render ( output ).toString ().replace ( "\n", "" ); 135 | assertTrue ( result.equals ("\u2830\u2800") ); 136 | } 137 | catch ( Exception e ) { 138 | assertTrue ( false ); 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/io/raffi/drawille/BrailleMap.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * This class stores a 4 by 2 pixel matrix that is eventually translated into a braille character. 7 | * This method abstracts away all the calculations that is needed to transform a matrix into a 8 | * braille character. This class is meant to be used as a sub-matrix. 9 | * @version 1.0.3 10 | * @package io.raffi.drawille 11 | * @author Rafael Grigorian 12 | * @copyright 2018 Rafael Grigorian — All Rights Reserved 13 | * @license MIT License 14 | */ 15 | public class BrailleMap { 16 | 17 | /** 18 | * @var Integer UNICODE_OFFSET Braille characters unicode offset 19 | * @var Integer [] TRANSFORM_MATRIX Transformation matrix for braille 20 | * @var Boolean [] map Flattened pixel map matrix (4 by 2) 21 | */ 22 | protected final static int UNICODE_OFFSET = 10240; 23 | protected final static int [] TRANSFORM_MATRIX = { 1, 8, 2, 16, 4, 32, 64, 128 }; 24 | protected Boolean [] map; 25 | 26 | /** 27 | * This constructor initializes the pixel map matrix and resets the matrix by initializing the 28 | * values with false. 29 | */ 30 | public BrailleMap () { 31 | this.map = new Boolean [ 8 ]; 32 | this.reset (); 33 | } 34 | 35 | /** 36 | * This method takes in a horizontal and vertical component and checks to see if it is in range 37 | * of the pixel matrix. Since braille can be expressed by a 4 by 2 dot matrix, these bounds are 38 | * taken to be the upper bound respectively while negative numbers are taken as the lower bound. 39 | * @param Integer x Horizontal coordinate 40 | * @param Integer y Vertical coordinate 41 | * @return void 42 | */ 43 | protected void checkRange ( int x, int y ) { 44 | if ( x < 0 || y < 0 || x > 1 || y > 3 ) { 45 | throw new DrawilleException ( x, y ); 46 | } 47 | } 48 | 49 | /** 50 | * This method takes in a horizontal and vertical coordinates alongside a matrix entry value. It 51 | * then sets said value into the pixel matrix based on the passed coordinates 52 | * @param Integer x Horizontal coordinate 53 | * @param Integer y Vertical coordinate 54 | * @param Boolean value The value to set matrix entry to 55 | * @return void 56 | */ 57 | public void change ( int x, int y, Boolean value ) { 58 | this.checkRange ( x, y ); 59 | this.map [ y * 2 + x ] = value; 60 | } 61 | 62 | /** 63 | * This method takes in a horizontal and vertical coordinates and it returns the value that is 64 | * saved in the pixel matrix based on the passed coordinates. 65 | * @param Integer x Horizontal coordinate 66 | * @param Integer y Vertical coordinate 67 | * @return Boolean Saved state based on coordinates 68 | */ 69 | public Boolean get ( int x, int y ) { 70 | this.checkRange ( x, y ); 71 | return this.map [ y * 2 + x ]; 72 | } 73 | 74 | /** 75 | * This method takes in a horizontal and vertical coordinates, it then activates the value into 76 | * the pixel matrix based on the passed coordinates. 77 | * @param Integer x Horizontal coordinate 78 | * @param Integer y Vertical coordinate 79 | * @return void 80 | */ 81 | public void set ( int x, int y ) { 82 | this.change ( x, y, true ); 83 | } 84 | 85 | /** 86 | * This method takes in a horizontal and vertical coordinates, it then deactivates the value 87 | * into the pixel matrix based on the passed coordinates. 88 | * @param Integer x Horizontal coordinate 89 | * @param Integer y Vertical coordinate 90 | * @return void 91 | */ 92 | public void unset ( int x, int y ) { 93 | this.change ( x, y, false ); 94 | } 95 | 96 | /** 97 | * This method takes in a horizontal and vertical coordinates, it then toggles the value in the 98 | * pixel matrix based on the passed coordinates. 99 | * @param Integer x Horizontal coordinate 100 | * @param Integer y Vertical coordinate 101 | * @return void 102 | */ 103 | public void toggle ( int x, int y ) { 104 | this.change ( x, y, !this.get ( x, y ) ); 105 | } 106 | 107 | /** 108 | * This method traverses through the pixel map matrix and deactivates all the pixels in the 109 | * matrix by setting all the values to false. 110 | * @return void 111 | */ 112 | public void reset () { 113 | Arrays.fill ( this.map, Boolean.FALSE ); 114 | } 115 | 116 | /** 117 | * This method traverses through the pixel map matrix and transforms the matrix into a braille 118 | * character. The resulting character is returned in string value. 119 | * @return String Pixel matrix as braille character 120 | */ 121 | public String toString () { 122 | int decimal = BrailleMap.UNICODE_OFFSET; 123 | for ( int i = 0; i < 8; i++ ) { 124 | if ( this.map [ i ] ) { 125 | decimal += BrailleMap.TRANSFORM_MATRIX [ i ]; 126 | } 127 | } 128 | return Character.toString ( ( char ) decimal ); 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /src/main/java/io/raffi/drawille/Turtle.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | /** 4 | * This class inherits from the Canvas class and it tries to implement turtle graphics. The methods 5 | * in this class can be abstracted with an idea of a pen and paper. One can move the pen in three 6 | * axis and based on the z axis, when the pen moves it either draws on the paper or not. 7 | * @version 1.0.3 8 | * @package io.raffi.drawille 9 | * @author Rafael Grigorian 10 | * @copyright 2018 Rafael Grigorian — All Rights Reserved 11 | * @license MIT License 12 | */ 13 | public class Turtle extends Canvas { 14 | 15 | /** 16 | * @param Double x Horizontal coordinate 17 | * @param Double y Vertical coordinate 18 | * @param Double a Angle 19 | * @param Boolean isDrawing Is the pen down? 20 | */ 21 | protected double x = 0; 22 | protected double y = 0; 23 | protected double a = 0; 24 | protected Boolean isDrawing = false; 25 | 26 | /** 27 | * This constructor simply calls the super constructor and passes the desired dimensions. 28 | * @param Integer width Desired width of canvas 29 | * @param Integer width Desired height of canvas 30 | */ 31 | public Turtle ( int width, int height ) { 32 | super ( width, height ); 33 | } 34 | 35 | /** 36 | * This method simply returns the horizontal component of the position of the pen. 37 | * @return double Horizontal position of pen 38 | */ 39 | public double getX () { 40 | return this.x; 41 | } 42 | 43 | /** 44 | * This method simply returns the vertical component of the position of the pen. 45 | * @return double Vertical position of pen 46 | */ 47 | public double getY () { 48 | return this.y; 49 | } 50 | 51 | /** 52 | * This method simply returns the angle component of the position of the pen. 53 | * @return double Angle of pen 54 | */ 55 | public double getAngle () { 56 | return this.a; 57 | } 58 | 59 | /** 60 | * This method simply sets the state of the pen to be in the drawing state. 61 | * @return void 62 | */ 63 | public void down () { 64 | this.isDrawing = true; 65 | } 66 | 67 | /** 68 | * This method simply sets the state of the pen to be in the non-drawing state. 69 | * @return void 70 | */ 71 | public void up () { 72 | this.isDrawing = false; 73 | } 74 | 75 | /** 76 | * This method takes in the angle to add to the right and adds it to the pen angle. 77 | * @param double angle Angle to move right 78 | * @return void 79 | */ 80 | public void right ( double angle ) { 81 | this.a += angle; 82 | } 83 | 84 | /** 85 | * This method takes in the angle to add to the left and subtracts it from pen angle. 86 | * @param double angle Angle to move right 87 | * @return void 88 | */ 89 | public void left ( double angle ) { 90 | this.a -= angle; 91 | } 92 | 93 | /** 94 | * This method takes in the length and expects us to move backwards with the pen based on the 95 | * current pen angle. The forward method is used to implement this method by simply passing in 96 | * the same scalar value of length but in the opposite direction. 97 | * @param double length Length to move back 98 | * @return void 99 | */ 100 | public void backward ( double length ) { 101 | this.forward ( length * -1 ); 102 | } 103 | 104 | /** 105 | * This method takes in the length and expects us to move forwards with the pen based on the 106 | * current pen angle. 107 | * @param double length Length to move forward 108 | * @return void 109 | */ 110 | public void forward ( double length ) { 111 | double theta = this.a / 180.0 * Math.PI; 112 | double x = this.x + length * Math.cos ( theta ); 113 | double y = this.y + length * Math.sin ( theta ); 114 | this.move ( x, y ); 115 | } 116 | 117 | /** 118 | * This method takes in a new horizontal and vertical coordinate and based on the current 119 | * coordinates, it draws a line to connect them. If the state of isDrawing is false, then we do 120 | * not draw and instead just move the pen to those coordinates. 121 | * @param double x Horizontal coordinate 122 | * @param double y Vertical coordinate 123 | * @return void 124 | */ 125 | public void move ( double x, double y ) { 126 | if ( this.isDrawing ) { 127 | int x1 = ( int ) Math.round ( this.x ); 128 | int y1 = ( int ) Math.round ( this.y ); 129 | int x2 = ( int ) Math.round ( x ); 130 | int y2 = ( int ) Math.round ( y ); 131 | int deltaX = Math.max ( x1, x2 ) - Math.min ( x1, x2 ); 132 | int deltaY = Math.max ( y1, y2 ) - Math.min ( y1, y2 ); 133 | int directionX = x1 <= x2 ? 1 : -1; 134 | int directionY = y1 <= y2 ? 1 : -1; 135 | int a = Math.max ( deltaX, deltaY ); 136 | for ( int i = 0; i <= a; i++ ) { 137 | int x0 = x1; 138 | int y0 = y1; 139 | if ( deltaY > 0 ) { 140 | y0 += ( ( double ) i * deltaY ) / a * directionY; 141 | } 142 | if (deltaX > 0) { 143 | x0 += ( ( double ) i * deltaX ) / a * directionX; 144 | } 145 | this.set ( x0, y0 ); 146 | } 147 | } 148 | this.x = x; 149 | this.y = y; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/io/raffi/drawille/Canvas.java: -------------------------------------------------------------------------------- 1 | package io.raffi.drawille; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * This class is used to hold all the BrailleMap objects and uses them as sub-matrices. It is an 9 | * abstraction of a pixel screen. Methods to interact with those pixels can be found in this class. 10 | * @version 1.0.3 11 | * @package io.raffi.drawille 12 | * @author Rafael Grigorian 13 | * @copyright 2018 Rafael Grigorian — All Rights Reserved 14 | * @license MIT License 15 | */ 16 | public class Canvas { 17 | 18 | /** 19 | * @var Byte [] lineEndingBytes New line bytes 20 | * @var Integer width Width of the canvas 21 | * @var Integer height Height of the canvas 22 | * @var BrailleMap [] screen Flattened screen matrix 23 | */ 24 | private byte [] lineEndingBytes = System.lineSeparator().toString ().getBytes (); 25 | protected int width; 26 | protected int height; 27 | protected BrailleMap [] screen; 28 | 29 | /** 30 | * This constructor takes in a width and height and initializes a flattened matrix of BrailleMap 31 | * objects. These objects serve as sub-matrices and extend the 'pixel' definition that can be 32 | * displayed on a screen. 33 | * @param Integer width The desired width of the canvas 34 | * @param Integer height The desired height of the canvas 35 | */ 36 | public Canvas ( int width, int height ) { 37 | this.width = width; 38 | this.height = height; 39 | this.screen = new BrailleMap [ this.width * this.height ]; 40 | for ( int i = 0; i < this.width * this.height; i++ ) { 41 | this.screen [ i ] = new BrailleMap (); 42 | } 43 | } 44 | 45 | /** 46 | * This method takes in a horizontal and vertical component and checks to see if it is in range 47 | * of the screen matrix. Since braille can be expressed by a 3 by 2 dot matrix, these bounds are 48 | * taken to be the upper bound respectively while negative numbers are taken as the lower bound. 49 | * These values are taken into effect by the getWidth and getHeight methods. 50 | * @param Integer x Horizontal coordinate 51 | * @param Integer y Vertical coordinate 52 | * @return void 53 | */ 54 | protected void checkRange ( int x, int y ) { 55 | if ( x >= this.getWidth () || y >= this.getHeight () || x < 0 || y < 0 ) { 56 | throw new DrawilleException ( x, y ); 57 | } 58 | } 59 | 60 | /** 61 | * This method returns the screen width in the true pixel definition. The user supplied width is 62 | * multiplied by 2 because a braille dot matrix has 2 columns. 63 | * @return Integer True pixel width 64 | */ 65 | public int getWidth () { 66 | return this.width * 2; 67 | } 68 | 69 | /** 70 | * This method returns the screen height in the true pixel definition. The user supplied height 71 | * is multiplied by 4 because a braille dot matrix has 4 rows. 72 | * @return Integer True pixel width 73 | */ 74 | public int getHeight () { 75 | return this.height * 4; 76 | } 77 | 78 | /** 79 | * This method takes in a horizontal and vertical coordinate and returns the value of the 80 | * activation of said pixel. If true, the pixel is turned on, otherwise it is off. 81 | * @param Integer x Horizontal coordinate of pixel 82 | * @param Integer y Vertical coordinate of pixel 83 | * @return Boolean The activation value of the pixel 84 | */ 85 | public Boolean get ( int x, int y ) { 86 | this.checkRange ( x, y ); 87 | BrailleMap map = this.screen [ ( ( y / 4 ) * this.width ) + ( x / 2 ) ]; 88 | return map.get ( x % 2, y % 4 ); 89 | } 90 | 91 | /** 92 | * This method takes in a horizontal and vertical coordinate as well as an activation state. It 93 | * then applies that activation to said pixel that lives in the passed coordinates. 94 | * @param Integer x Horizontal coordinate of pixel 95 | * @param Integer y Vertical coordinate of pixel 96 | * @param Boolean value Activation to set on pixel 97 | * @return void 98 | */ 99 | public void change ( int x, int y, Boolean value ) { 100 | this.checkRange ( x, y ); 101 | BrailleMap map = this.screen [ ( ( y / 4 ) * this.width ) + ( x / 2 ) ]; 102 | map.change ( x % 2, y % 4, value ); 103 | } 104 | 105 | /** 106 | * This method takes in a horizontal and vertical coordinate, it then activates said pixel by 107 | * setting it's value to true. 108 | * @param Integer x Horizontal coordinate of pixel 109 | * @param Integer y Vertical coordinate of pixel 110 | * @return void 111 | */ 112 | public void set ( int x, int y ) { 113 | this.change ( x, y, true ); 114 | } 115 | 116 | /** 117 | * This method takes in a horizontal and vertical coordinate, it then deactivates said pixel by 118 | * setting it's value to false. 119 | * @param Integer x Horizontal coordinate of pixel 120 | * @param Integer y Vertical coordinate of pixel 121 | * @return void 122 | */ 123 | public void unset ( int x, int y ) { 124 | this.change ( x, y, false ); 125 | } 126 | 127 | /** 128 | * This method takes in a horizontal and vertical coordinate, it then toggles the activation of 129 | * said pixel based on the value that it currently has. 130 | * @param Integer x Horizontal coordinate of pixel 131 | * @param Integer y Vertical coordinate of pixel 132 | * @return void 133 | */ 134 | public void toggle ( int x, int y ) { 135 | this.change ( x, y, !this.get ( x, y ) ); 136 | } 137 | 138 | /** 139 | * This method traverses through all the BrailleMap objects that is stored to make up the 140 | * screen, it then resets all the values in those sub-matrices. 141 | * @return void 142 | */ 143 | public void clear () { 144 | for ( int i = 0; i < this.width * this.height; i++ ) { 145 | this.screen [ i ].reset (); 146 | } 147 | } 148 | 149 | /** 150 | * This method traverses through all the BrailleMap objects and renders out the sub-matrices by 151 | * asking for the object's string value with the getString method. It then prints them all out 152 | * to the screen by using the overloaded cooresponding render method. 153 | * @return void 154 | */ 155 | public void render () { 156 | ByteArrayOutputStream stream = new ByteArrayOutputStream (); 157 | try { 158 | this.render ( stream ); 159 | } 160 | catch ( IOException e ) { 161 | System.out.print ( e ); 162 | } 163 | System.out.print ( stream.toString () ); 164 | } 165 | 166 | /** 167 | * This method traverses through all the BrailleMap objects and renders out the sub-matrices by 168 | * asking for the object's string value with the getString method. It then writes said output to 169 | * the specified ByteArrayOutputStream. This stream is then returned back to caller for method 170 | * chaining. 171 | * @param ByteArrayOutputStream stream Stream to write to 172 | * @return ByteArrayOutputStream Same stream that was passed in 173 | * @throws IOException ByteArrayOutputStream throws exception 174 | */ 175 | public ByteArrayOutputStream render ( ByteArrayOutputStream stream ) throws IOException { 176 | for ( int i = 0; i < this.width * this.height; i++ ) { 177 | String brailleMap = this.screen [ i ].toString (); 178 | byte [] buffer = brailleMap.getBytes (); 179 | stream.write ( buffer ); 180 | if ( i % this.width == this.width - 1 ) { 181 | stream.write ( lineEndingBytes ); 182 | } 183 | } 184 | return stream; 185 | } 186 | 187 | } 188 | --------------------------------------------------------------------------------