Berlin-Clock

Übersicht: Python & Blender


Objectives

The Berlin-Uhr(R), invented by Dieter Binninger. The clock was installed in 1975 for the first time at the Kurfürstendamm in Berlin-Charlottenburg near the subway station Uhlandstr.The Berlin-Uhr is based on the principles of quantity didactics and with this its the first of its kind. The time is calculated by adding the lit rectangular lamps. The top lamp is a pump which is blinking on/off every two seconds. In the upper line of red lamps every lamp represents 5 hours. In the lower line of red lamps every lamp represents 1 hour. So if in the first line 2 lamps are lit and in the second line 3 lamps its 5+5+3=13h or 1 p.m. In the third line with tall lamps every lamp represents 5 minutes. There are 11 lamps, the 3rd, 6th, and 9th are red indicating the first quarter, half, and the last quarter of the hour. In the last line with yellow lamps every lamp represents 1 minute.the "Berliner Uhr" or Berlin-Clock...

Instructions

Tasks:
  1. have a look at the different sources to get the idea:

    http://www.surveyor.in-berlin.de/berlin/uhr/indexe.html

  2. start developing the code for your own version of the clock

  3. use three colors like the origin clock

  4. add a world and a sky to set the background color use the appropriate lines from the chapter Blender extended, Camera "trac to empty"

  5. try to solve one of the three open bonus versions

Arrange the elements of the clock

We will not create the clock with the skin of the origin as you can see in the picture, instead we are creating an abstract version of the clock. So we use the basic elements of blender -- a cube.

Lets start with an array of hashmarks where each element represents a lamp.

use-cases/informatics/berlin-clock/berlin_clock_001.py (Source)

1
2
3
4
5
clock = ["#",
         "####",
         "####",
         "##########",
         "####"]

Now for each hashmark a cube will be created. We use for loops and index values for this task.

use-cases/informatics/berlin-clock/berlin_clock_001.py (Source)

    rows = len(object)

    for row in range(rows):
        cols = len(object[row])
        for col in range(cols):
            bpy.ops.mesh.primitive_cube_add(location=(row, col, 0))

if __name__ == '__main__':
    bpy.ops.object.select_by_type(type='MESH')
    bpy.ops.object.delete()

Test the first Version...

  • start a fresh blend file
  • switch the Layout from Default to Scripting
  • create a new Script berlin_clock.py
  • run the script with Alt+P
  • check the result
    • Blender started from a console (Linux, MacOS)
    • menue »System Console« (Windows)
/use-cases/informatics/berlin-clock/berlin-clock10.png

Justify the distances between all elements

So lets add some blender units. We use a list of constants for each row. With this values we calculate x and y, the z value is allways 0.

use-cases/informatics/berlin-clock/berlin_clock_002.py (Source)

1
2
3
4
5
6
7
8
    distances = [9.5, 3.75, 3.75, 1.6, 3.75]
    for row in range(rows):
        cols = len(object[row])
        for col in range(cols):
            distance = distances[row]
            x = row * 3
            y = col * distance + distance
            bpy.ops.mesh.primitive_cube_add(location=(x, y, 0))

Final positioning

To shorten the lines (pep8) we calculate the values in some extra lines. As you can see the origin clock elements for minutes are smaller, so we scale down all cubes of this row.

/use-cases/informatics/berlin-clock/berlin-clock20.png

use-cases/informatics/berlin-clock/berlin_clock_003.py (Source)

            if row == 3:
                bpy.ops.transform.resize(value=(0.5, 0.5, 0.5))
/use-cases/informatics/berlin-clock/berlin-clock30.png

Names for objects

Later on we want to change the color of the elements, therefor it is a good idea to use unique names. We replace the hashmarks with lists of names.

use-cases/informatics/berlin-clock/berlin_clock_004.py (Source)

1
2
3
4
5
6
7
clock = [["seconds"],
         ["hour_01", "hour_02", "hour_03", "hour_04"],
         ["hour_05", "hour_06", "hour_07", "hour_08"],
         ["minute_01", "minute_02", "minute_03", "minute_04"] +
         ["minute_05", "minute_06", "minute_07", "minute_08"] +
         ["minute_09", "minute_10", "minute_11"],
         ["minute_12", "minute_13", "minute_14", "minute_15"]]

and change the standard names with our own names...

use-cases/informatics/berlin-clock/berlin_clock_004.py (Source)

            obj = bpy.context.object
            obj.name = clock[row][col]

Where can I see the names? Have a look at the Outliner in the upper right corner of Blender.

/use-cases/informatics/berlin-clock/berlin-clock40.png

Material and Color

A color code is used to show the time. Lets colorize the objects.

A special function is used to set the color. Later on you can define additional properties.

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

1
2
3
4
def setColor(obj, material, color):
    material.diffuse_color = color
    material.specular_hardness = 200
    obj.data.materials.append(material)

A color is a property of a material, so lets define one.

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

MATERIAL_001 = bpy.data.materials.new('Mat001')

Afterwards we can set the color (at the moment one color for all objects).

use-cases/informatics/berlin-clock/berlin_clock_005.py (Source)

            setColor(obj, MATERIAL_001, (1, 0, 1))
/use-cases/informatics/berlin-clock/berlin-clock50.png

Color time specific parts

We need the actual time and have to extract all parts of the time. For each part of the time, we set the name for hours, minutes and seconds in a dictionary.

Coloring with the correct time

We need a structure with all parts of our clock. Therefor we call a function named timing in a module witch also has the name timing.

As you can see we get a time object. With dir(t), you can see what information the datetime object is delivering. In all cases we use comparisons to get a True or False. All values are returned as one dictionary.

use-cases/informatics/berlin-clock/timing.py (Source)

"""
Name: 'berlin-clock.py'
Blender: 27x
Group: 'Example'
Tooltip: 'Creating the berlin-clock'
"""
import datetime


def timing():
    '''
    extrakt all parts of a timeobject
    '''
    result = {}
    t = datetime.datetime.now()

    # seconds
    result["seconds"] = t.second % 2 == 0

    # 5-hour parts
    result["hour_01"] = (t.hour > 4)
    result["hour_02"] = (t.hour > 9)
    result["hour_03"] = (t.hour > 14)
    result["hour_04"] = (t.hour > 19)

    # 1-hour parts
    h = t.hour % 5
    result["hour_05"] = (h > 0)
    result["hour_06"] = (h > 1)
    result["hour_07"] = (h > 2)
    result["hour_08"] = (h > 3)

    # 5-minute parts
    result["minute_01"] = (t.minute > 4)
    result["minute_02"] = (t.minute > 9)
    result["minute_03"] = (t.minute > 14)
    result["minute_04"] = (t.minute > 19)
    result["minute_05"] = (t.minute > 24)
    result["minute_06"] = (t.minute > 29)
    result["minute_07"] = (t.minute > 34)
    result["minute_08"] = (t.minute > 39)
    result["minute_09"] = (t.minute > 44)
    result["minute_10"] = (t.minute > 49)
    result["minute_11"] = (t.minute > 54)

    # 1-minute parts
    m = t.minute % 5
    result["minute_12"] = (m > 0)
    result["minute_13"] = (m > 1)
    result["minute_14"] = (m > 2)
    result["minute_15"] = (m > 3)

    return result


if __name__ == '__main__':
    timing()

Back to our main script we can call the new function and use the dictionary.

First the import for the timing module.

use-cases/informatics/berlin-clock/berlin_clock_006.py (Source)

from timing import timing

We call the scene object and the timing function from the timing module. With all values from the dictionary the color will be changed for the object with the given name, if the value is True.

use-cases/informatics/berlin-clock/berlin_clock_006.py (Source)

1
2
3
4
5
6
7
    res = timing()
    scn = bpy.context.scene
    for name in res.keys():
        scn.objects.active = scn.objects[name]
        obj = bpy.context.object
        if res[name]:
            setColor(obj, MATERIAL_001, (1, 0, 1))

Test the clock

  • load both scripts (timing.py and berlin-clock.py) into the scripting area of Blender
  • switch to the berlin-clock.py and run the script (Alt+P)
/use-cases/informatics/berlin-clock/berlin-clock60.png

Using the Game-Engine

To let the clock update regularly, we need a second timing function. For games and simulations a clock-function is available in the blender game engine. We need a third script to run the clock permanently with the blender game engine.

use-cases/informatics/berlin-clock/update001.py (Source)

from bge import logic
from timing import timing

res = timing()

# get the scene
scene = logic.getCurrentScene()

# coall the objects in the scene
for name in res.keys():
    scene.objects[name].color = (1, 0, 1, .7) \
                                if res[name] else (1, .5, 1, 1)

Last step: include the script with the BGE (Blender Game engine).

  • select the camera (right mouse click)
  • switch the window type "Python console" at the bottom to "Logic Editor"
  • add a new Always sensor
  • add a Python controller
  • add the script update.py to the controller
  • connect the sensor with the controller (drag & drop)
  • set Level triggering to True
  • select Game Engine in the top menu (instaed of Blender Render)
  • Optional switch to the full view for the 3D-Window
  • Start the clock (game) with the menu or press in the 3D-View P
/use-cases/informatics/berlin-clock/berlin-clock70.png

Bonus I

Add random colors with each start...

use-cases/informatics/berlin-clock/berlin_clock_008.py (Source)

1
2
3
4
5
6
color_a = random.uniform(0.3, 1)
color_b = random.uniform(0.3, 1)
color_c = random.uniform(0.3, 1)
color_d = random.uniform(0.3, 1)
color_e = random.uniform(0.3, 1)
color_f = random.uniform(0.3, 1)

Each color is a mix of tree base colors: red, green, blue. With the random module we get one value and use this for colorizing all elements of the clock. This is the improved script update.py:

use-cases/informatics/berlin-clock/update002.py (Source)

from bge import logic
from timing import timing

from berlin_clock_008 import color_a
from berlin_clock_008 import color_b
from berlin_clock_008 import color_c
from berlin_clock_008 import color_d
from berlin_clock_008 import color_e
from berlin_clock_008 import color_f

res = timing()

# get the scene
scene = logic.getCurrentScene()

# coall the objects in the scene
for name in res.keys():
    scene.objects[name].color = (color_a, color_b, color_c, .7) \
                                if res[name] else (color_d,
                                                   color_e,
                                                   color_f, 1)

Comments