koxelaにページ切り替えエフェクトをつけてみた。
ということで一応リリース(?)
ソースほとんど変わってないけど、もういいや。めんどい。
ソースはこちら
あとデモ用のkxlファイルも上げておいたのであわせてどぞ。デモファイル
ということで、性懲りもせずにテキストです。
紹介自体は前回のエントリに書いたんで、切り替えエフェクトの説明のみで。
とりあえず実装したエフェクトが四種類。
まず、なんもせずただ順に書いていくcutin。
カットインとはいっても結局ターミナルの描画速度に依存するのでそんな「カット」ってイメージがない...(д
あとランダムディゾルブ...っていうのカナ?
各点をひたすらにランダムに埋めていく方法。
ライン毎ランダム。多分ランダムストライプ(横)とか名前だった気がする。pptでは。
フェードは...えっとなんと書けばいいのやら。
AAAA AAAA AAAA
が
ABAB BABA ABAB
になって
BBBB BBBB BBBB
になる、と。
なぜかsortで実装しているキモさをソース読んで堪能ください(w
どうでもいいけど、デモファイルの背景がグラデのところ。
id:Hossyに98時代っぽいと言われた...orz
そーす。
#!/usr/bin/env ruby # koxela Ver.0.0.1 - こけら # テキストプレゼンテーションツール # # usage: # $ koxela demo.kxl # $ ./demo # # Warning: # Koxela creates file "demo" in this case without any alert # even if file "demo" is already exits. # # - 8<- - - - - - - - - - - - - - - - - # Copyright (C) 2008 TAKEI Yuya All rights reserved. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # require "cairo" require "yaml" class Display end class FileDisplay < Display def initialize fp, col, row @fp = fp @fp.puts %<#!/bin/sh> @fp.puts %<# col:#{col} row:#{row}> @fp.puts %<STTY_DEFAULT=`stty -g`> @fp.puts %<stty -echo> end def finish @fp.puts %<stty ${STTY_DEFAULT}> end def clear @fp.puts %<echo -en "\033[2J"> end def rewind @fp.puts %<echo -en "\033[1;1H"> end def keywait @fp.puts %<read> end def wait n=1 @fp.puts %<usleep #{n*1000*1000}> end def right n=1 @fp.puts %<echo -en "\033[#{n}C"> end def reset @fp.puts %<reset> end def putStr str str.gsub!( '"', %<\"> ) @fp.puts %<echo -en "#{str}"> end def putStrLn str str.gsub!( '"', %<\"> ) @fp.puts %<echo -e "#{str}"> end def move x, y @fp.puts %<echo -en "\033[#{y};#{x}H"> end def color r,g,b end end def rgb2y rr,gg,bb rr * 0.299 + gg * 0.587 + bb * 0.114 end def rgb2esc rr,gg,bb "\033[38;5;%dm" % ( (rr * 6 / 256) * 36 + (gg * 6 / 256) * 6 + (bb * 6 / 256) + 16 ) end def grids col,row points = [] row.times { |y| col.times { |x| points += [[x,y]] } } points end TIOCGWINSZ = 0x00005413 TIOCSWINSZ = 0x00005414 str = "" $stdin.ioctl( TIOCGWINSZ, str ) (row, col) = str.unpack("S!4") if ARGF.filename == "-" then exit end yaml = YAML.load( ARGF.read ) width = yaml["common"]["width"] || 600 height = yaml["common"]["height"] || 400 bg = yaml["common"]["background"] || [0, 0, 0] fg = yaml["common"]["foreground"] || [1, 1, 1] chars = yaml["common"]["chars"] || " .,~=+?I7Z08DNM#" fontName = yaml["common"]["font"]["name"] || "" fontSize = yaml["common"]["font"]["size"] || 400 lineWidth = yaml["common"]["line"]["width"] || 10 effect = yaml["common"]["effect"] || "cutin" delay = yaml["common"]["delay"] || 0.1 pages = yaml["pages"] || [] surface = Cairo::ImageSurface.new( col, row ) context = Cairo::Context.new( surface ) context.scale( col / width.to_f, row / height.to_f ) basename = File.basename ARGF.filename, ".*" File.open( "#{basename}", "w" ){ |fp| disp = FileDisplay.new( fp, col, row ) disp.clear pages.each_with_index{ |page,i| context.set_source_rgb( bg ).rectangle( 0, 0, width, height ).fill context.save page["objects"].each{ |obj| case obj["type"] when "line" context.set_source_rgb( obj["color"] || fg ) context.move_to( obj["pos1"][0], obj["pos1"][1] ) context.line_to( obj["pos2"][0], obj["pos2"][1] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.stroke end when "circle" context.set_source_rgb( obj["color"] || fg ) context.circle( obj["pos"][0], obj["pos"][1], obj["radius"] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.fill end when "rect" context.set_source_rgb( obj["color"] || fg ) context.rectangle( *obj["rect"] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.stroke end when "string" context.select_font_face( obj["font"] || fontName, 0, 0 ) context.set_font_size( obj["size"] || fontSize ) context.set_source_rgb( obj["color"] || fg ) context.set_line_width( obj["width"] || lineWidth ) ext = context.text_extents( obj["string"] ) extWidth = ext.x_advance - ext.x_bearing extHeight = ext.y_advance - ext.y_bearing context.move_to( obj["pos"][0] - extWidth / 2, obj["pos"][1] + extHeight / 2) context.text_path( obj["string"] ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.fill end else end } # jabanner化 jabanner = [] row.times { |y| jabanner[y] = [] col.times { |x| jabanner[y][x] = "" rr = surface.data[4*(x + y * col) + 2] gg = surface.data[4*(x + y * col) + 1] bb = surface.data[4*(x + y * col) + 0] jabanner[y][x] += rgb2esc( rr, gg, bb ) jabanner[y][x] += chars[chars.length * rgb2y( rr, gg, bb ) / 256].chr } } animationDelay = page["delay"] || delay case (page["effect"] || effect) when "fade" # フェード {} grids( col, row ).sort_by{|tmp0,tmp1|(tmp0+tmp1)%2}.each{ |x,y| disp.move x, y disp.putStr jabanner[y][x] } disp.wait animationDelay when "rand" # ランダム {} grids( col, row ).sort_by{rand}.each{ |x,y| disp.move x, y disp.putStr jabanner[y][x] } disp.wait animationDelay when "randline" # ランダムライン {} (0..(row-1)).sort_by{rand}.each { |y| disp.move 0, y col.times { |x| disp.putStr jabanner[y][x] } disp.wait animationDelay } else # カットイン disp.rewind row.times { |y| col.times { |x| disp.putStr jabanner[y][x] } } end disp.keywait context.restore } surface.finish disp.reset } # vim: sw=2:ts=2:sts=2:foldmethod=syntax