Horizontal Digital Clock

SleepyOz has been at it again and has a new version of his digital clock, but now renders it horizontally instead. Check out the picture and the code below!

 

 

#! /usr/bin/python
import mcpi.block as block
import mcpi.minecraft as minecraft
import time

"""
Dot matrix digits 5 wide by 8 high.
0 - voxel should be drawn
Anything else - voxel should be cleared
"""
digit_dots = {
   '0':[
' 000',
'0   0',
'0   0',
'0   0',
'0   0',
'0   0',
'0   0',
' 000',
],
   '1':[
'  0',
' 00',
'  0',
'  0',
'  0',
'  0',
'  0',
' 000',
],
   '2':[
' 000',
'0   0',
'   0',
'  0',
' 0',
'0',
'0',
'00000',
],
   '3':[
' 000',
'0   0',
'    0',
'  00',
'    0',
'    0',
'0   0',
' 000',
],
   '4':[
'   0',
'  00',
' 0 0',
'0  0',
'00000',
'   0',
'   0',
'   0',
],
   '5':[
'00000',
'0',
'0',
'0000',
'    0',
'    0',
'0   0',
' 000',
],
   '6':[
' 000',
'0   0',
'0',
'0000',
'0   0',
'0   0',
'0   0',
' 000',
],
   '7':[
'00000',
'    0',
'   0',
'  0',
' 0',
'0',
'0',
'0',
],
   '8':[
' 000',
'0   0',
'0   0',
' 000',
'0   0',
'0   0',
'0   0',
' 000',
],
   '9':[
' 000',
'0   0',
'0   0',
' 0000',
'    0',
'    0',
'0   0',
' 000',
],
   ':':[
'',
'',
'  00',
'  00',
'',
'  00',
'  00',
'',
],
}

class buffer:
   """
   Double-buffer a voxel message for Minecraft.
   To improve performance, only changes are rendered.
   """
   anchor_position = minecraft.Vec3(0,0,0)
   last_message = ''
   offscreen = []
   onscreen = []
   unset = block.OBSIDIAN
   set = block.SNOW_BLOCK

   def __init__(self, anchor_position):
      """
      Set everything up to render messages into the world
      at the given position.
      """
      self.anchor_position = anchor_position

   def draw_base(self, client):
      """
      Build some foundations for the clock.
      """
      # Foundations of stone.
      for y in range(-5, -1): # Nice thick base.
         for x in range(-1, 8*6): # 8 digits each 6 wide, plus a border, minus one to cut out the space beside the last digit.
            for z in range(-1, 8+1): # Each digit is 8 high, plus a border.
               client.setBlock(self.anchor_position.x+x, self.anchor_position.y+y, self.anchor_position.z+z, block.STONE)
      # Shallow pool at the top.
      for x in range(0, 8*6-1):
         for z in range(0, 8):
            client.setBlock(self.anchor_position.x+x, self.anchor_position.y-2, self.anchor_position.z+z, block.WATER)

   def render(self, message):
      """
      Put message into the off-screen buffer.
      """
      if message != self.last_message: # Do nothing if the message has not changed.
         self.last_message =  message # For next time.

         self.offscreen = [] # Clear any previous use of the buffer.
         letter_offset = 0
         for letter in message:
            rendition = digit_dots[letter]
            line_offset = 0
            for line in rendition:
               if len(self.offscreen) <= line_offset:
                  # Make space to store the drawing.
                  self.offscreen.append([])
               dot_offset = 0
               for dot in line:
                  if dot == '0':
                     self.offscreen[line_offset].append(self.set)
                  else:
                     self.offscreen[line_offset].append(self.unset)
                  dot_offset += 1
               for blank in range(dot_offset, 6):
                  # Expand short lines to the full width of 6 voxels.
                  self.offscreen[line_offset].append(self.unset)
               line_offset += 1
            letter_offset += 1

         # Clear the onscreen buffer.
         # Should only happen on the first call.
         # Assumption: message will always be the same size.
         # Assumption: render() is called before flip().
         if self.onscreen == []:
            # No onscreen copy yet - so make it the same size as the offscreen image. Fill with suitable voxels.
            line_offset = 0
            for line in self.offscreen:
               self.onscreen.append([])
               for dot in line:
                  self.onscreen[line_offset].append(block.DIRT)
               line_offset += 1

   def flip(self, client):
      """
      Put the off-screen buffer onto the screen.
      Only send the differences.
      Remember the new screen for next flip.
      Draw the clock inverted so it read properly from above.
      """
      line_offset = 0
      height = len(self.offscreen) - 1
      for line in self.offscreen:
         dot_offset = 0
         length = len(line) - 2 # Fit into the border better.
         for dot in line:
            if self.onscreen[line_offset][dot_offset] != dot:
               self.onscreen[line_offset][dot_offset] = dot
               client.setBlock(self.anchor_position.x+length-dot_offset, self.anchor_position.y-3, self.anchor_position.z+height-line_offset, dot)
            dot_offset += 1
         line_offset += 1

client=minecraft.Minecraft.create() # Connect to Minecraft.
place=client.player.getPos() # Start near the player.
# place.y is just below the player, and we don't need to change it.
bitmapper = buffer(place)

bitmapper.draw_base(client)
while True:
   timestr = time.strftime("%H:%M:%S") # Format time nicely.
   bitmapper.render(timestr)
   bitmapper.flip(client)
   time.sleep(.1) # Rest a while before drawing again.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s