歴史をプログラミングで?

動機

先日kotaronくんが学校の宿題で歴史に勉強をしている所をみていたら、ふと?、歴史とプログラミングで何かできないかな?という興味が湧いてきました。算数や美術、音楽とプログラミングは簡単に結びつくけど、歴史って結びつけづらいなぁと。逆にやる気が湧いてきたわけです!

教科書を拝借してペラペラめくってみたら💡💡💡速攻ネタ発見!そうだ!プログラミングで年表を表示しよう!

先日、木曜日クラスで外部テキストに記述されたデータ(Pokemon!)をプログラミングに取り込む授業をしたので、同じように歴史の年表を記述した外部ファイルを取り込むと、年表が表示されるプログラムを作ることにしました。

概要案

  • 外部ファイルに、年号、出来事、時代をCSV形式で記述
  • プログラムで取り込みリストに格納
  • 軸線やメモリを描画
  • 画面下部に年号を描画
  • メイン部分に出来事を描画

ソースコード

fontSize = 10

enable3D = False

paddingTop = 50
paddingRight = 50
paddingBottom = 100
paddingLeft = 50

dotPerYear = 5

gridWidth = dotPerYear * 20
gridHeight = 460 # setupで画面サイズから再計算

yearStart = 1900
yearPre = None
timeline = None

level = 0
LEVEL_MAX = 10

COLOR_BACKGROUND = color(191, 140, 129)
COLOR_BACKGROUND_ALPHA = color(191, 140, 129, 180)
COLOR_TEXT = color(255)
COLOR_SUB_LIGHT = color(255, 206, 192)
COLOR_SUB_DARK = color(114, 83, 77)

def setFont(n = 1):
    global fontSize
    if n == 1:
        fontSize = 20
        font = createFont(u"JFドットM+10 Regular", fontSize, False)
    elif n == 2:
        fontSize = 14
        font = createFont(u"JFドット東雲ゴシック14 Regular", fontSize, False)
    textFont(font)

def bubbleText(t, x, y, align=LEFT):
    textAlign(align)
    textSize(fontSize)
    noStroke()
    if enable3D:
        fill(COLOR_TEXT)
    else:
        fill(COLOR_TEXT)
        if align == LEFT:
            rect(x - 4, y - fontSize, textWidth(t) + 6, fontSize * 1.3, 4) # 職人芸で微調整
        elif align == RIGHT:
            rect(x - 4 - textWidth(t), y - fontSize, textWidth(t) + 6, fontSize * 1.3, 4) # 職人芸で微調整
        else:
            pass
        fill(COLOR_BACKGROUND)
    text(t, x, y)

def setEvent(e, y, isText):
    global level, nowEra, yearPre
    eYear = int(e[0])
    eText = e[1]
    x = paddingLeft + (eYear - yearStart) * dotPerYear

    # 左端の時代が見えない場合の補助
    if len(e[2]) > 0 and x < paddingLeft:
        nowEra = e[2]

    if x >= paddingLeft and x <= width - paddingRight:

        stroke(114, 83, 77)
        fill(COLOR_TEXT)
        noFill()

        lineHeight = fontSize * level * 2

        if isText:
            # 時代名
            textAlign(LEFT)
            textSize(fontSize)
            text(e[2], x, y + fontSize * 2.4)

            # 左端の時代が見えない場合の補助を表示する場所がない場合はナシにする
            if len(e[2]) > 0 and x < paddingLeft + textWidth(nowEra):
                nowEra = ""

            # 年
            tX = x + 10 + 4 + 8
            tY = y - lineHeight + fontSize / 2
            bubbleText(e[0], tX, tY)

            # 出来事名
            tX += textWidth(e[0]) + fontSize / 2
            if enable3D == False:
                fill(COLOR_BACKGROUND_ALPHA)
                rect(tX, tY - fontSize, textWidth(eText), fontSize * 1.3)
            textAlign(LEFT)
            textSize(fontSize)
            fill(COLOR_TEXT)
            text(eText, tX, tY)

        else:
            # 引出線
            if eYear != yearPre: # 同年の出来事がある場合は描画しない
                ellipse(x, y, 3, 3)
                line(x, y, x + 10, y - lineHeight)
                line(x + 10, y - lineHeight, x + 10 + 4, y - lineHeight)

    yearPre = eYear
    level -= 1
    if level < 1 :
        level = LEVEL_MAX



# ===================================================

def setup():
    size(1000, 600, P3D)

    global timeline, gridHeight
    gridHeight = height - paddingTop - paddingBottom
    timeline = loadStrings("timeline.txt")
    setFont(1)

def draw():
    global level, yearStart, dotPerYear, gridWidth
    background(COLOR_BACKGROUND)

    # カメラ
    if enable3D:
        x = mouseX
        if mouseX > width / 7 * 6:
            x = width / 7 * 6
        if mouseX < width / 7:
            x = width / 7
        camera(x, height/2, (height/2) / tan(PI/6), width/2, height/2, 0, 0, 1, 0);
    else:
        camera(width/2.0, height/2.0, (height/2.0) / tan(PI*30.0 / 180.0), width/2.0, height/2.0, 0, 0, 1, 0)

    # X軸線
    fill(COLOR_TEXT)
    stroke(COLOR_TEXT)
    x1 = paddingLeft
    y1 = paddingTop + gridHeight + 10
    x2 = width - paddingRight
    y2 = paddingTop + gridHeight + 10
    line(x1, y1, x2, y2)

    # 刻み線
    textAlign(CENTER)
    textSize(fontSize)
    xStart = paddingLeft + (ceil(yearStart / gridWidth) * gridWidth - yearStart) * dotPerYear
    count = yearStart + (ceil(yearStart / gridWidth) * gridWidth -yearStart)
    for i in range(xStart, width - paddingRight + 1, gridWidth):
        if i >= x1 and i <= x2:
            stroke(175, 128, 118)
            line(i, y1 - 1, i, y1 - gridHeight)
            stroke(255)
            if count % 100 == 0:
                line(i, y1 + 2, i, y1 - 5)
                fill(COLOR_TEXT)
                text(count, i, y1 + 2 + fontSize * 0.8)
            else:
                line(i, y1, i, y1 + 2)
                fill(255, 206, 192)
                text(count, i, y1 + 2 + fontSize)
        count += (gridWidth / dotPerYear)

    # 罫線と被るので手前に
    translate(0, 0, 1)

    # 出来事(引出線)
    level = LEVEL_MAX
    for tl in timeline:
        setEvent(tl.split(","), y1, False)

    # 出来事(文字)
    level = LEVEL_MAX
    for tl in timeline:
        setEvent(tl.split(","), y1, True)

    # 時代補助
    if len(nowEra) > 0:
        textAlign(LEFT)
        fill(COLOR_TEXT)
        text(nowEra, paddingLeft, y1 + fontSize * 2.4)

    # タイトル
    fill(COLOR_TEXT)
    textSize(fontSize)
    textAlign(CENTER)
    text(u"日本史年表", width / 2, paddingTop / 2 + fontSize / 2)

    # 縮尺表示
    bubbleText(str(gridWidth / dotPerYear), width - 20, paddingTop / 2 + fontSize / 2, RIGHT)

    # キー操作
    if keyPressed == False:
        return


    # フォント変更
    if key == "1":
        setFont(1)
    elif key == "2":
        setFont(2)

    # 左右スライド
    if keyCode == LEFT:
        # yearStart -= 1
        yearStart -= 60 / dotPerYear
    if keyCode == RIGHT:
        # yearStart += 1
        yearStart += 60 / dotPerYear

    # 縮尺変更
    if keyCode == UP or keyCode == DOWN:

        if frameCount % 5 != 0:
            return

        if keyCode == UP:
            dotPerYear +=1
        if keyCode == DOWN:
            dotPerYear -=1

        # 縮尺上限下限チェック
        if dotPerYear > 60:
            dotPerYear = 60
        if dotPerYear < 2:
            dotPerYear = 2

        # 縮尺に応じてgridWidth再計算
        if dotPerYear < 10:
            gridWidth = dotPerYear * 20
        elif dotPerYear < 15:
            gridWidth = dotPerYear * 10
        elif dotPerYear < 24:
            gridWidth = dotPerYear * 5
        elif dotPerYear < 46:
            gridWidth = dotPerYear * 2
        else:
            gridWidth = dotPerYear * 1

def mousePressed():
    global enable3D, paddingRight, paddingLeft
    enable3D = not(enable3D)
    if enable3D:
        paddingRight = -1450
        paddingLeft = -1450
    else:
        paddingRight = 50
        paddingLeft = 50

ダウンロード

2件のコメント

コメントする