ascii-art-to-unicode-0.1.0.1: ASCII Art to Unicode Box Drawing converter

Safe HaskellSafe
LanguageHaskell2010

Text.AsciiArt

Contents

Description

Basic Elements

Simple Boxes

>>> :{
aa2u "++  +-----+  +--+--+-----+  \n\
     \++  +--+  |  |  |  |     |  \n\
     \       |  |  +--+--+--+--+  \n\
     \+---+  |  |  |     |  |  |  \n\
     \+---+  +--+  +-----+--+--+  "
:}
┌┐  ┌─────┐  ┌──┬──┬─────┐
└┘  └──┐  │  │  │  │     │
       │  │  ├──┴──┼──┬──┤
┌───┐  │  │  │     │  │  │
└───┘  └──┘  └─────┴──┴──┘

Rounded Boxes

>>> :{
aa2u "..  .-----.  .--+--+-----.  \n\
     \''  '--.  |  |  |  |     |  \n\
     \       |  |  +--+--+--+--+  \n\
     \.---.  |  |  |     |  |  |  \n\
     \'---'  '--'  '-----+--+--'  "
:}
╭╮  ╭─────╮  ╭──┬──┬─────╮
╰╯  ╰──╮  │  │  │  │     │
       │  │  ├──┴──┼──┬──┤
╭───╮  │  │  │     │  │  │
╰───╯  ╰──╯  ╰─────┴──┴──╯

Dotted and double strokes

>>> :{
aa2u "++  .-----.  +==+==+=====+  \n\
     \++  +==+  :  |  :  |     |  \n\
     \       :  :  +==+==+==+==+  \n\
     \+===+  :  :  |     |  :  |  \n\
     \+---+  '--'  +=====+==+==+  "
:}
┌┐  ╭─────╮  ╒══╤══╤═════╕
└┘  ╘══╕  ┆  │  ┆  │     │
       ┆  ┆  ╞══╧══╪══╤══╡
╒═══╕  ┆  ┆  │     │  ┆  │
└───┘  ╰──╯  ╘═════╧══╧══╛

Cast shadows

>>> :{
aa2u "+-------------+   \n\
     \|             |   \n\
     \+---+     +---+#  \n\
     \  ##|     |#####  \n\
     \    |     |#      \n\
     \    +-----+#      \n\
     \      ######      "
:}
┌─────────────┐
│             │
└───┐     ┌───┘█
  ██│     │█████
    │     │█
    └─────┘█
      ██████

Properties

Idempotent

Already rendered portions are not affected:

>>> :{
aa2u "┌┐  ╭─────╮  ╒══╤══╤═════╕  ┌───┐   \n\
     \└┘  ╘══╕  ┆  │  ┆  │     │  │   │   \n\
     \       ┆  ┆  ╞══╧══╪══╤══╡  │   │█  \n\
     \╒═══╕  ┆  ┆  │     │  ┆  │  └───┘█  \n\
     \└───┘  ╰──╯  ╘═════╧══╧══╛    ████  "
:}
┌┐  ╭─────╮  ╒══╤══╤═════╕  ┌───┐
└┘  ╘══╕  ┆  │  ┆  │     │  │   │
       ┆  ┆  ╞══╧══╪══╤══╡  │   │█
╒═══╕  ┆  ┆  │     │  ┆  │  └───┘█
└───┘  ╰──╯  ╘═════╧══╧══╛    ████

Incremental

Existing characters can be removed:

>>> :{
aa2u "┌──┬ ┬──┐\n\
     \   │ │  │\n\
     \╞══╪ ╪══╡\n\
     \│        \n\
     \├──┼ ┼──┤\n\
     \   │ │  │\n\
     \└──┴ ┴──┘"
:}
───┐ ┌──┐
   │ │  │
╒══╛ ╘══╛
│
└──┐ ┌──┐
   │ │  │
───┘ └──┘

New connections can be added:

>>> :{
aa2u "┌──┐-┌──┐\n\
     \│  │ │  │\n\
     \╘══╛=╘══╛\n\
     \|  : :  |\n\
     \┌──┐-┌──┐\n\
     \│  │ │  │\n\
     \└──┘-└──┘"
:}
┌──┬─┬──┐
│  │ │  │
╞══╪═╪══╡
│  ┆ ┆  │
├──┼─┼──┤
│  │ │  │
└──┴─┴──┘

Existing connections can be altered by replacing/adding characters:

>>> :{
aa2u "┌──+──┐  .─────+─────.  ┌────┐    \n\
     \│  +==+  │     |     │  │####│    \n\
     \+==+  |  │     |     │  │#   │█   \n\
     \└──+─-┘  │     +-----+  └────┘█#  \n\
     \         +=====+     │    █████#  \n\
     \╭──+─-╮  │     |     │     #####  \n\
     \│  |  |  │     |     │            \n\
     \╰──+──╯  '───────────'            "
:}
┌──┬──┐  ╭─────┬─────╮  ┌────┐
│  ╞══╡  │     │     │  │████│
╞══╡  │  │     │     │  │█   │█
└──┴──┘  │     ├─────┤  └────┘██
         ╞═════╡     │    ██████
╭──┬──╮  │     │     │     █████
│  │  │  │     │     │
╰──┴──╯  ╰───────────╯

Limitations

Some connections do not work as expected (mostly because the corresponding Unicode characters do not exist), e.g. rounded corners with double-stroke lines, or connection pieces connecting horizontal single- and double-stroke lines:

>>> :{
aa2u "--+==  .==.  .--.--.   \n\
     \  |    |  |  |  |  |   \n\
     \==+--  '=='  .--+--'   \n\
     \  |          |  |  |   \n\
     \--+==  --==  '--'--'   "
:}
──┐══  ╒══╕  ╭──╮──╮
  │    │  │  │  │  │
══├──  ╘══╛  ╰──┼──╯
  │          │  │  │
──┘══  ──══  ╰──╰──╯

Synopsis

Zipper

data Zipper a Source #

The Zipper is assumed to be infinite, i.e. filled with empty values outside the defined area.

Constructors

Zipper 

Fields

Instances

Functor Zipper Source # 

Methods

fmap :: (a -> b) -> Zipper a -> Zipper b #

(<$) :: a -> Zipper b -> Zipper a #

Comonad Zipper Source # 

Methods

extract :: Zipper a -> a #

duplicate :: Zipper a -> Zipper (Zipper a) #

extend :: (Zipper a -> b) -> Zipper a -> Zipper b #

zipperToList :: Int -> Zipper a -> [a] Source #

Renders the current and the n - 1 elements after as list.

zipperOf :: a -> Zipper a Source #

An infinite Zipper filled with as.

zipperFromList :: a -> [a] -> Zipper a Source #

Takes a list and creates a Zipper from it. The current element will be the head of the list, and after that tail. The rest will be filled with as to an infinite Zipper.

Plane (two-dimensional Zipper)

newtype Plane a Source #

A plane is a Zipper of Zippers. The outer layer zips through lines (up/down), the inner layer through columns (left/right). Like the Zipper, the Plane is assumed to be infinite in all directions.

Constructors

Plane 

Fields

Instances

Functor Plane Source # 

Methods

fmap :: (a -> b) -> Plane a -> Plane b #

(<$) :: a -> Plane b -> Plane a #

Comonad Plane Source # 

Methods

extract :: Plane a -> a #

duplicate :: Plane a -> Plane (Plane a) #

extend :: (Plane a -> b) -> Plane a -> Plane b #

planeToList :: Int -> Int -> Plane a -> [[a]] Source #

Renders m lines and n columns as nested list.

planeOf :: a -> Plane a Source #

An infinite Plane filled with as.

planeFromList :: a -> [[a]] -> Plane a Source #

Create a Plane from a list of lists, filling the rest with as in all directions.

Patterns

newtype Pattern Source #

Constructors

Pattern ((Char, Char, Char), (Char, Char, Char), (Char, Char, Char)) 

lookupPattern :: Pattern -> Maybe Char Source #

Find the Char to replace the center of a Pattern.

connectsLike :: Char -> Char -> Bool Source #

Whether a character can connect to another character. For example, + connects both horizontally (like -) and vertically (like |), so it connectsLike -, |, and of course like itself.

patterns :: [(Pattern, Char)] Source #

The actual pattern definitions. For convenience, the simple patterns are at the top, and more complex ones at the bottom. lookupPattern will first try the most complex pattern and work its way to the simpler patterns, thus avoiding to choose a simpler pattern and forgetting some connection.

Transforming ASCII to Unicode

substituteChar :: Plane Char -> Char Source #

Match the current element and its eight neighbours against the defined patterns and choose the Char from the matching Pattern.

renderAsciiToUnicode :: Plane Char -> Plane Char Source #

Transform a Plane of ASCII characters to an equivalent plane where the ASCII box drawings have been replaced by their Unicode counterpart.

This function is a convolution with substituteChar using the Comonadic extend.

>>> :{
aa2u :: String -> IO ()
aa2u input =
    let inputLines = lines input
        plane = planeFromList ' ' inputLines
        width = maximum (fmap length inputLines)
        height = length inputLines
    in  putStr
        . unlines
        . fmap (reverse . dropWhile (== ' ') . reverse)
        . planeToList height width
        . renderAsciiToUnicode
        $ plane
:}