Braille graphics

46

3

Cut a boolean matrix in 4x2 blocks and render them as Braille characters U+2800...U+28FF.

[[0,1,0,0,1,0],
 [1,0,0,0,0,0],
 [1,0,0,0,1,0],
 [1,1,1,1,0,0]]

⣎⣀⠅

Pad with 0-s if dimensions are not multiples of 4 and 2.

[[0,1,0],
 [1,0,0],
 [1,1,1]]

⠮⠄

Usual golfing rules apply, flexible on input format. Output should either have the structure of a matrix or look like a matrix, e.g. list of strings; single string with newlines.

Hint: chr(0x2800 + 128*b7 + 64*b6 + 32*b5 + 16*b4 + 8*b3 + 4*b2 + 2*b1 + b0) is the dot pattern

b0 b3
b1 b4
b2 b5
b6 b7

Larger test:

[[0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0],
 [0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1],
 [0,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,0,1,1,0,0,0,1],
 [1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1],
 [1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,0,1,0],
 [1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0],
 [1,1,0,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0],
 [1,1,0,1,1,1,1,1,0,0,1,1,0,0,1,0,0,1,1,1,1,1,1],
 [1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,0],
 [1,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,0,1,1,0,0],
 [1,1,0,0,0,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,1,0,0],
 [1,1,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,0,1,1,0,0],
 [0,1,1,0,1,1,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0],
 [0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,1,1,0,0],
 [0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,0,1,1,1,1,0]]

⣰⠟⠻⣦⠀⠠⠾⡇⢠⡞⢛⡆
⣿⢠⣬⣥⠄⣀⠀⡇⢈⣻⣈⡀
⣿⠘⢹⡇⡞⠙⡇⣧⡉⢹⡏⠀
⠘⠷⠟⠁⠳⠾⠃⠘⠇⠾⠧⠀

ngn

Posted 2017-08-27T10:06:28.497

Reputation: 11 449

Congrats on second challenge. – Adám – 2017-08-27T10:49:12.747

5Better description: you have a 2D array of Boolean values whose rows represents the horizontal raster lines of a black-and-white (1 bit per pixel) frame buffer or graphics canvas. Encode all of the 4x2 rectangular blocks of this canvas into Unicode Braille characters. To handle fractional blocks at the edges, pad the canvas's width to a multiple of 2, and height to a multiple of four, with zeros (or otherwise ensure the equivalent output, treating the data as if it were so padded). – Kaz – 2017-08-27T17:44:57.487

3@Kaz I don't know, I personally really appreciate how concise this post is. IMO, not a lot of clarity would be added by writing any more (besides a few small clarifications like noting that the height should be a mult of 4 and the width of 2); your suggestion is harder for me to read than the current post. – Quelklef – 2017-09-10T19:43:54.210

Answers

10

Jelly,  31  30 bytes

sz0Z
ç€2ZF€ç€8Zœ?@€€-36Ḅ+⁽$ṁỌY

Try it online!

How?

sz0Z - Link 1, split & right-pad with zeros: list, items; number, chunkSize
s    - split items into chunks of length chunkSize
 z0  - transpose with filler zero
   Z - transpose

ç€2ZF€ç€8Zœ?@€€-36Ḅ+⁽$ṁỌY - Main link: list of lists of numbers (1s & 0s), M
ç€2                       - call the last link (1) as a dyad for €ach (left=M, right=2)
                          -  ((left,right) bits read left-right then top-bottom)
   Z                      - transpose the resulting list of lists of lists
                          -  ((left, right) bits read top-bottom then left-right)
    F€                    - flatten €ach
      ç€8                 - call the last link (1) as a dyad for €ach (left=^, right=8)
         Z                - transpose the resulting list of lists of lists
                          -  ("blocks" each and all read left-right top-to bottom)
               -36        - literal -36
             €€           - for €ach (block-wise row) for €ach (block)
          œ?@             -   lexicographical permutation with reversed arguments
                          -    (get the permutation at index -36 (modular) in a list of
                          -     all permutations of the indexes sorted lexicographically.
                          -     That is the 8!-36 = 40284th - equivalently the values at
                          -     indexes [8,7,6,4,2,5,3,1])
                  Ḅ       - convert from binary list to integer (vectorises)
                    ⁽$ṁ   - base 250 literal = 10240
                   +      - add
                       Ọ  - cast to character (vectorises)
                        Y - join with newlines
                          - implicit print

Jonathan Allan

Posted 2017-08-27T10:06:28.497

Reputation: 67 804

Does support "digits" greater than 1? Instead of adding 10240 (0x2800 - two bytes) to the result, you could prepend 40 (0x28 - one byte) to the vector of binary digits. I don't know much about Jelly, so I'm not sure if this would really work. – ngn – 2017-08-27T17:22:21.327

would indeed convert a leading digit of 40 as you suggest, but we'd need to prepend it to each such list (at a depth of 2) which, I think, would require more bytes of code (;@€€40Ḅ). – Jonathan Allan – 2017-08-27T17:28:58.437

6

JavaScript ES7 210 207 201 200 198 194 185 183 bytes

a=>eval('for(y=0,c="";A=a[y];y+=4,c+=`\n`)for(x=0;A[x]+1;x+=2)c+=String.fromCharCode(10240+eval("for(N=k=0;k<6;k++)N+=(g=(X,Y=3)=>(a[Y+y]||0)[X+x]|0)(k>2,k%3)*2**k")|g(0)+g(1)*2<<6)')

4 bytes saved thanks to ngn

3 bytes saved thanks to Luke

How it works

I'm going to split the code up into parts and speak about them separately:

for(y=x=0, c=""; a[y]; x+=2)
    !((a[y] || [])[x]+1) && (y+=4,x=0,c+=`\n`)

This is where every variable gets declared. x and y is the position of the "cursor" (the upper left edge of the current braille character). The x coordinate increases by 2 every iteration, and it stops, when there's no row with the index y (a[x] returns undefined if it doesn't exist, which gets converted to false).

There are multiple tricks in the second row. (a[y] || [])[x] ensures that looking up the value at the (x, y) position doesn't throw an error. The && is the usual and operator, and it only checks the right side of the expression, if the left was true. This can be translated to

if (!((a[y] || [])[x] + 1)) 
    y+=4,x=0,c+=`\n`

The next part:

c+=String.fromCharCode(10240+eval("for(N=k=0;k<6;k++)N+=(g=(x,y)=>(a[y]||[])[x]||0)(~~(k/3)+x,k%3+y)*2**k,N")+g(x,y+3)*64+g(x+1,y+3)*128)

String.fromCharCode simply converts the passed number to a unicode character with the same character code. The expression in the parentheses calculates the index of the Braille character:

for(N=k=0;k<6;k++)N+=(g=(x,y)=>(a[y]||[])[x]||0)(~~(k/3)+x,k%3+y)*2**k

Goes through the position in the

1 4
2 5
3 6

order, multiplies the values at those positions with 2i, where i is the index and adds them together. The

g=(x,y)=>(a[y]||[])[x]||0

part declares a lambda function called g, which given an x and y coordinate returns either the value at the (x, y) position or 0 if the position lies outside the bounds of the array.

+g(x,y+3)*64+g(x+1,y+3)*128

This part adds up the last two positions with the correct weights using the function defined a bit earlier.

Last but not least, the

a=>eval('...')

part has 2 functions. It defines an anonymous lambda and ensures, that the for loop doesn't cause any problems (a single line lambda such as this one can't contain a single for loop only, an evaluation circumvents this).

Bálint

Posted 2017-08-27T10:06:28.497

Reputation: 1 847

a few simple suggestions: ||0 -> |0; ~~(k/3) -> (k>2); *128 -> <<7 (replacing +-s with |-s) – ngn – 2017-08-27T17:09:50.437

Why not submit the ES7 version as your primary solution? – Shaggy – 2017-08-27T17:10:49.143

@Shaggy Not everyone can run ES7 yet, so it's a backup – Bálint – 2017-08-27T17:12:45.330

That's irrelevant 'round these parts ;) As long as there is one, single interpreter (browser) that can run your code properly, it's considered to be valid here. – Shaggy – 2017-08-27T17:15:28.177

@ngn Thanks for the first two, but bit shifting has a lower precedence than basically anything, so that won't work – Bálint – 2017-08-27T17:17:04.377

@Shaggy I started with ES6, but then realized I could save a couple of bytes with a technique and I could utilize ES7 there, but here you go, removed – Bálint – 2017-08-27T17:21:41.713

Sorry, shaky hands on a touchscreen posted my last comment before I could add: Submit the ES7 one as your main solution and, if you feel it's worth it, the ES6 one as the backup. – Shaggy – 2017-08-27T17:25:52.343

@JonathanAllan Thanks, forgot to add the next one – Bálint – 2017-08-27T17:31:35.377

I saved 10 bytes: a=>eval('for(y=x=0,c="";A=a[y]||[];A[x+=2]<0||(y+=4,x=0,c+="\n"))c+=String.fromCharCode(10240+eval("for(N=k=0;++k<6;)N+=(g=(X,Y=3)=>(a[Y+y]||[])[X+x]|0)(k>2,k%3)*2**k")+g(0)*64+g(1)*128)') – Luke – 2017-08-27T17:46:11.350

@Bálint as I mentioned in brackets, for <<7 you'd have to replace some +-s with |-s: ...+g(0,3)*64+g(1,3)*128... -> ...|g(0,3)*64|g(1,3)<<7...; shifting has higher precedence than bitwise or - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table

– ngn – 2017-08-27T19:23:56.207

@Luke that doesn't really work. If you insert ||[] after A=a[y], then the loop won't stop. Thanks for the Y=3 one though – Bálint – 2017-08-27T19:55:51.160

@ngn Right, fixed – Bálint – 2017-08-27T19:55:59.403

6

Mathematica, 126 110 97 90

FromCharacterCode[10240+ListCorrelate[2^{{0,3},{1,4},{2,5},{6,7}},#,1,0][[;;;;4,;;;;2]]]&

This solution takes advantage of ListCorrelate to convolve a (reversed) kernel over a matrix, which is essentially a sliding matrix multiplication (or dot product). See a visual explanation here. Padding is done by using 0 as the fourth argument. In the following example, we expect the result to match the hint above:

ListCorrelate[
  2^{{0, 3}, {1, 4}, {2, 5}, {6, 7}},
  {{b0, b3}, {b1, b4}, {b2, b5}, {b6, b7}}
]

(* returns {{b0 + 2 b1 + 4 b2 + 8 b3 + 16 b4 + 32 b5 + 64 b6 + 128 b7}} *)

Note that ListConvolve is not any shorter, since the third argument would be -1.

Since this applies the kernel at every position of the matrix, we just need to extract the elements in every fourth row and second column. We use shorthands for Span and Part: [[;;;;4,;;;;2]].

Helpfully, FromCharacterCode can take a matrix of character codes and return a list of strings.


This solution returns a list of strings, which is one of the allowed output formats. Simply prepend Column@ for the output to “look like a matrix.”


You can play around with this in a free online Mathematica notebook. Go here, click Create a New Notebook, wait a moment, paste in this code, then press shift+enter.

m1={{0,1,0,0,1,0},{1,0,0,0,0,0},{1,0,0,0,1,0},{1,1,1,1,0,0}};
m2={{0,1,0},{1,0,0},{1,1,1}};
m3={{0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,0},{0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1},{0,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,0,1,1,0,0,0,1},{1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1},{1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,0,1,0},{1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0},{1,1,0,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0},{1,1,0,1,1,1,1,1,0,0,1,1,0,0,1,0,0,1,1,1,1,1,1},{1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,0},{1,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,0,1,1,0,0},{1,1,0,0,0,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,1,0,0},{1,1,0,0,0,1,1,0,1,0,0,0,1,0,1,1,1,0,0,1,1,0,0},{0,1,1,0,1,1,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,1,1,0,0},{0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,0,1,1,1,1,0}};

MatrixToBraille := Column@
  FromCharacterCode[10240+ListCorrelate[2^{{0,3},{1,4},{2,5},{6,7}},#,1,0][[;;;;4,;;;;2]]]&

MatrixToBraille/@{m1,m2,m3}

Then you should see this:

hftf

Posted 2017-08-27T10:06:28.497

Reputation: 436

5

Dyalog APL, 133 122 114 112 101 100 98 95 94 93 90 88 86 bytes

Assumes ⎕IO←0

{C⍴{⎕UCS 10240+2⊥(∊S⌷⍨⍵+⍳¨A)[⍎¨⍕76531420]}¨(,b)/,⍳⍴b←{0 0≡A|⍵}¨⍳⍴S←⍵↑⍨A×C←⌈(⍴⍵)÷A←4 2}

-8 9 12 bytes thanks to @Adám in chat

-2 bytes thanks to @ngn

Try it online!

How (input is )?

  • A←4 2, store the vector 4 2 in variable A
  • (⍴⍵)÷, the dimensions of divided by A
  • , ceiling
  • C←, stored in C
  • , multiplied by A
  • ⍵↑⍨, fit to those dimensions
  • S←, stored in S
  • ⍳⍴, indexes of S
  • {0 0≡A|⍵}¨, 1 where the top left of a cell is, 0 everywhere else
  • (,b)/,⍳⍴b←, truthy indexes
  • {⎕UCS 10240+2⊥(∊S⌷⍨⍵+⍳¨A)[⍎¨⍕76531420]}¨, turn each element into braille
  • C⍴, reshape the dimensions to C

Zacharý

Posted 2017-08-27T10:06:28.497

Reputation: 5 710

consider +/(2*0 3 1,A,4+⍳3)×something2⊥something[⍎¨⍕76524130] – ngn – 2017-08-27T17:30:03.970

WOuld that still work now that I changed it to ⎕IO←0? – Zacharý – 2017-08-27T17:31:20.693

Actually, it would only work in ⎕IO←0 :) – ngn – 2017-08-27T17:36:21.427

Sorry, I forgot about this stupid thing (⎕IO) in APL. For ⎕IO←1, of course you'd have to add 1 to each digit of 76524130. – ngn – 2017-08-27T17:38:37.097

Are those something's different? – Zacharý – 2017-08-27T17:44:13.677

It seems something is fine, but the permutation (76524130) must be different. – ngn – 2017-08-27T17:49:32.350

it should work with 76531420 – ngn – 2017-08-27T17:58:35.467

4

Python 2 + drawille, 141 125 120 116 bytes

Saved 16 bytes thanks to ngn and L3viathan

Saved 5 bytes thanks to L3viathan

Saved 4 bytes thanks to ngn

from drawille import*
def a(d,c=Canvas(),e=enumerate):[c.set(j,i)for i,x in e(d)for j,y in e(x)if y];print c.frame()

Try it online!

tio does not have drawille installed so it does not work

Noskcaj

Posted 2017-08-27T10:06:28.497

Reputation: 421

Python and its batteries! :) Doesn't fail to surprise me. You can shrink this to under 120 bytes if you use enumerate() and a list comprehension. – ngn – 2017-08-28T06:52:47.630

Save a couple of bytes by making the function a one-liner: def b(d,c=l.Canvas()):print([c.set(j,i)for i,x in enumerate(d)for j,y in enumerate(x)if y]and c).frame() – L3viathan – 2017-08-28T10:32:52.473

Got it to 120 bytes – L3viathan – 2017-08-28T11:08:35.650

you don't need the and c trick - the comprehension could be a statement on its own followed by ;print c.frame() – ngn – 2017-08-28T11:34:10.330

4

JavaScript, 136 bytes

a=>(b=a.map(x=>[]),a.map((l,i)=>l.map((c,j)=>b[i>>2][j>>1]|=c<<'01263457'[i%4+j*4%8])),b.map(l=>l.map(c=>String.fromCharCode(10240+c))))

Thanks to ngn, using bit shifts save 4 bytes.

tsh

Posted 2017-08-27T10:06:28.497

Reputation: 13 072

you can use bit shifts like i/4|0 -> i>>2 – ngn – 2017-08-28T08:44:57.473

c*2** is a bit shift, too :) – ngn – 2017-08-28T10:10:24.157

3

APL (Dyalog), 57 54 bytes*

-3 thanks to OP. Prompts for Boolean matrix. Prints character matrix.

1↓⎕UCS{2⊥40,⌽(,⍉3↑⍵),⊢⌿⍵}⌺(2 2⍴4 2)⊢0⍪⍣3⍪∘0⍣3⊢⎕,0

Try it online!

⎕,0 append a zero on right (ignored if even number of columns)

 yield that (to separate 3 and )

⍪∘0⍣3 append zeros on bottom thrice (because drops partial windows)

0⍪⍣3 stack zeros on top thrice (because starts in the top left corner)

 yield that (separates the parenthesis and the 0)

{}⌺(2 2⍴4 2) on each 4-row 2-column window, with 4-row vertical and 2 row horizontal step:

⊢⌿⍵ last row (lit. vertical right reduction); [b6,b7]

(), prepend:

  3↑ take three rows; [[b0,b3],[b1,b4],[b2,b5]]

   transpose; [[b0,b1,b2],[b3,b4,b5]]

  , ravel; [b0,b1,b2,b3,b4,b5]

 now we have [b0,b1,b2,b3,b4,b5,b6,b7]

 reverse; [b7,b6,b5,b4,b3,b2,b1,b0]

40, prepend 40 (for 40×29 = 10240); [40,b7,b6,b5,b4,b3,b2,b1,b0]

2⊥ evaluate as base-2 (binary)

⎕UCS convert to character

1↓ drop the first row (all-zero because of 's padding)


* In Classic, counting as ⎕U233A.

Adám

Posted 2017-08-27T10:06:28.497

Reputation: 37 779

There's an easy way to save a few bytes, see my comment under the Jelly solution. – ngn – 2017-09-10T11:36:09.200

There must be a mistake - the TIO link doesn't match the code you posted here. – ngn – 2017-09-10T15:33:21.087

it's the zero-padding code near the end: 0⍪⍣3⍪∘0⍣3⊢⎕,0 vs 0⍪∘0⍣3⊢⎕,0 – ngn – 2017-09-10T15:35:13.723

@ngn Fixed, but I have a feeling that ⍪∘0⍣3 and ,0 are only needed due to a bug in , and the first one isn't needed for your test cases. – Adám – 2017-09-10T15:56:28.697

My test cases aren't exhaustive - the solution should, of course, work for any valid input. You can shorten 0⍪⍣3⍪∘0⍣3⊢⍵,0 to 0(⊖⍪)⍣6⊢⍵,0. – ngn – 2017-09-10T16:19:11.717

2

Python 3, 168 165 161 bytes

def f(m):
 while m:
  r,m,s=[*zip(*m)],m[4:],''
  while r:s+=chr(10240+sum(q<<int(w)for(q,w)in zip((r[0]+(0,)*3)[:4]+(r+[()])[1],'01263457')));r=r[2:]
  print(s)

Try it online!

TFeld

Posted 2017-08-27T10:06:28.497

Reputation: 19 246

Nicely golfed! You can save three bytes with [*zip(*m[:4])] instead of calling list. – Lynn – 2017-08-28T11:42:45.717

2

Haskell, 145 bytes

(a!b)c=take b$c++repeat a
r([]:_)=[]
r l=['⠀'..]!!(sum.zipWith(*)[1,8,2,16,4,32,64,128]$l>>=0!2):r(drop 2<$>l)
b[]=[]
b s=r(([]!4)s):b(drop 4s)

Try it online!

Angs

Posted 2017-08-27T10:06:28.497

Reputation: 4 825

1

Python 3, 169 bytes

a=[]
y=0
for l in eval(input()):
 y-=1;a+=y%4//3*[-~len(l)//2*[10240]];x=0
 for v in l:a[-1][x//2]|=v<<(6429374>>y%4*6+x%2*3&7);x+=1
for l in a:print(*map(chr,l),sep='')

Try it online!

Lynn

Posted 2017-08-27T10:06:28.497

Reputation: 55 648

You can rewrite if y%4<1:a+=-~len(l)//2*[10240], as a+=(y%4<1)*[-~len(l)//2*[10240]] and fit x=0;y+=1 on the same line. I think it saves a byte. – ngn – 2017-08-28T17:46:10.253

@ngn saved a few more bytes from there on out, thank you! – Lynn – 2017-08-28T18:13:48.860

1

Perl 5, 164 bytes

163 bytes of code + 1 flag -p

@a=eval}{for(;$r<@a;$r+=4){for($c=0;$c<@{$a[0]};$c+=2){$n="0b";map$n.=0|$a[$r+3][$c+$_],1,0;for$y(1,0){map$n.=0|$a[$r+$_][$c+$y],2,1,0}$\.=chr 0x2800+oct$n}$\.=$/}

Try it online!

Takes each row comma separated on one line.

Xcali

Posted 2017-08-27T10:06:28.497

Reputation: 7 671

1

APL (Dyalog), 53 bytes

4 2∘{⎕ucs 2⊥40⍪(⍎¨⍕76531420)∘.⊃,¨⍺∘↑¨↓∘⍵¨⍺∘ר⍳⌈⍺÷⍨⍴⍵}

Try it online!

ngn

Posted 2017-08-27T10:06:28.497

Reputation: 11 449