著者名が「magicien
81〜90件目 / 211件
前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 次へ

 冷たい校舎の時は止まる / magicien 

冷たい校舎の時は止まる(上) (講談社文庫) 辻村深月さんのデビュー作。上下巻を一気に読んだ。

11月の記事が抜けてしまうなぁと思い、10月に読んだ本の感想でも書いてみる。
本屋で辻村さんの本が大量に平積みされていたので、とりあえずデビュー作から読んでみようということで、買ってみました。
買ってから気づいたけど、上下巻合わせると結構分厚い。
ノリオがときどき京極夏彦さんの本の感想を書いているので、どれどれと思って京極さんのコーナーに行くと、その本棚だけ異様な雰囲気を醸し出していますが、辻村さんの本も本棚に横に並べれば同じような空気になりそう。

内容は、ある朝登校した8人の高校生が時間の止まった校舎に閉じ込められて...という話で、ミステリーというよりホラーに近いかもしれません。
8人がそれぞれ抱えている悩みが話のメインになっていて、しかもその悩みが割とリアルだったりして、読んでいて古傷が痛んだりするわけで、死人が蘇るより、過去のトラウマが蘇る方がよほど恐ろしかったりするわけです。

ところで、この本では辻村深月という人物が出てくるけど、作者とこの人物は同じような性格ということなんだろうか。
2012/11/30(Fri) 23:00:00

 すべてがFになる / magicien 

すベてがFになる (講談社文庫) 森博嗣さんの作家としてのデビュー作であり、私が夏休みに読むはずだった1冊。本文を書きすぎてセッションが切れて、内容全部ふっとんだけど泣きながらもう一回書く。

冒頭で登場人物の紹介があり、プログラマが何人かいたので、おぉっ?と思って興味が湧いたと同時に、タイトルの意味について何となく察しがついた。
それにしても、作品の最初にオブジェクト指向の本を引用するのも驚いたし、「それでは、なぜ人間は交換をするのであろう」という文から始まり人間の脳の構造から説明をしているオブジェクト指向の本があるのにも驚いた。

私の勝手なイメージでは、森さんは機械工学には精通しているけど、ソフトウェア工学はあんまり、という感じだと思っていましたが、よくよく調べるとBASICやCの本も書いていたんですね。
小説を書く場合、テーマを決めてから、そのテーマについて詳しく調べていき、話の中身を考えるという作り方が一般的なのではないかと思う(ビブリア古書堂の古書なんかはそうだと思う)のだけど、森さんの場合は、既に持っている知識の中からテーマを選び、そこにミステリーの肉付けをしているような気がする。というより、書きたいことを書いて本にするためにミステリィの体裁をとっている、という印象。

それはさておき、本の感想に戻ると、プログラムやOSに関する話が随所に出て来て、わくわくしながら読めました。使っているエディタがemacsなのかviなのかは気になるところ。1995年に書かれた本ということで、内容は少し古いですが、それはそれで楽しめると思います。あるいは、書かれた年代が謎解きのヒントになるかもしれません。
それと、途中で部屋の中の工具を列挙していく場面があり、その気合いの入りっぷり、活き活きした感じはやはりというかさすがというか、先生、素が出てますよ!という感じで見所です。愛犬のトーマくんもちょろっと出演している模様。
このシリーズを読んだのはこれが初めてですが、作品の一番最後に登場した人物がシリーズ後半でのモリアーティ教授的存在になるのかなぁと思ったり。

2012/10/08(Mon) 17:09:03

 JavaScriptでテキストデータの読み込み / magicien 

JavaScriptでXファイルを読み込んだときに使った方法。

バイナリデータの読み込みについて書きかけの状態ですが、今回はテキストデータの読み込みについて。
実行速度よりも実装のしやすさを優先するなら、Ginがおすすめ。
実際の使い方としてはこんな感じ。
var xfileGrammar;
function start()
{
  xfileGrammar = new Gin.Grammar({
  XFile:         / XFileHeader:header (XObjectLong)* /,
  XFileHeader:   / 'xof ':xof VersionNumber FormatType:showValue FloatSize:showValue /,
  VersionNumber: / <\d\d>:showValue <\d\d>:showValue /,
  FormatType:    / 'txt ' | 'bin ' | 'tzip' | 'bzip' /,
  FloatSize:     / '0064' | '0032' /,
 
  XObject:       / Template | Animation | AnimationKey | AnimationOptions | AnimationSet | AnimTicksPerSecond | Boolean | Boolean2d | ColorRGB | ColorRGBA | Coords2d | DeclData | EffectDWorld | EffectFloats | EffectInstance | EffectParamDWorld | EffectParamFloats | EffectString | FaceAdjacency | FloatKeys | Frame | FrameTransformMatrix | FVFData | Header | IndexedColor | Material | Matrix4x4 | MeshFaceWraps | MeshFace | MeshMaterialList | MeshNormals | MeshTextureCoords | MeshVertexColors | Mesh | Patch | PatchMesh9 | PatchMesh | PMAttributeRange | PMInfo | PMVSplitRecord | SkinWeights | TextureFilename | TimedFloatKeys | Vector | VertexDuplicationIndices | VertexElement | XSkinMeshHeader /,
  XObjectLong:   / Template | AnimationLong | AnimationKeyLong | AnimationOptionsLong | AnimationSetLong | AnimTicksPerSecondLong | BooleanLong | Boolean2dLong | ColorRGBLong | ColorRGBALong | Coords2dLong | DeclDataLong | EffectDWorldLong | EffectFloatsLong | EffectInstanceLong | EffectParamDWorldLong | EffectParamFloatsLong | EffectStringLong | FaceAdjacencyLong | FloatKeysLong | FrameLong | FrameTransformMatrixLong | FVFDataLong | HeaderLong | IndexedColorLong | MaterialLong | Matrix4x4Long | MeshFaceWrapsLong | MeshFaceLong | MeshMaterialListLong | MeshNormalsLong | MeshTextureCoordsLong | MeshVertexColorsLong | MeshLong | PatchLong | PatchMeshLong | PatchMesh9Long | PMAttributeRangeLong | PMInfoLong | PMVSplitRecordLong | SkinWeightsLong | TextureFilenameLong | TimedFloatKeysLong | VectorLong | VertexDuplicationIndicesLong | VertexElementLong | XSkinMeshHeaderLong /,
 
  XString:       / <[0-9A-Za-z]*> /,
  Identifier:    / XString /,
  Name:          / XString /,
  UUID:          / '<' (<[0-9A-Za-z]|->)* '>' /,
  Member:        / ('array' TemplateType Name '[' ($INT | XString) ']') | (TemplateType Name) /,
  TemplateType:  / XString /,
  XArray:        / '[' $INT ']' /,
 
  XObjectTemplate:       / Identifier:identifier Name:name '{' UUID ('[...]' | ('[' Name ']') | (Member ';'))* '}' /,
  Template:             / 'template':template  Name '{' UUID ('[...]' | ('[' Name ']') | (Member ';'))* '}' /,
 
  IntValue:             / $INT ';' ::first /,
  IntArray:             / $INT (',' $INT)* (',')? ';' /,
 
  FloatValue:           / $REAL ';' ::first /,
  FloatArray:           / $REAL (',' $REAL)* (',')? ';' /,
 
  Header:               / HeaderLong | (HeaderValue ';') /,
  HeaderLong:           / 'Header' (Name)? '{' (UUID)? HeaderValue '}' /,
  HeaderValue:          / IntValue:major IntValue:minor IntValue:flags /,
 
  Animation:            / AnimationLong | ( AnimationValue ';') /,
  AnimationLong:        / 'Animation' (Name)? '{' (UUID)? AnimationValue '}' /,
  AnimationValue:       / $ANY /,
 
  AnimationKey:         / AnimationKeyLong | ( AnimationKeyValue ';') /,
  AnimationKeyLong:     / 'AnimationKey' (Name)? '{' (UUID)? AnimationKeyValue '}' /,
  AnimationKeyValue:    / $ANY /,
 
  AnimationOptions:      / AnimationOptionsLong | ( AnimationOptionsValue ';') /,
  AnimationOptionsLong:  / 'AnimationOptions' (Name)? '{' (UUID)? AnimationOptionsValue '}' /,
  AnimationOptionsValue: / $ANY /,
 
  AnimationSet:         / AnimationSetLong | ( AnimationSetValue ';') /,
  AnimationSetLong:     / 'AnimationSet' (Name)? '{' (UUID)? AnimationSetValue '}' /,
  AnimationSetValue:    / $ANY /,
 
  AnimTicksPerSecond:      / AnimTicksPerSecondLong | ( AnimTicksPerSecondValue ';') /,
  AnimTicksPerSecondLong:  / 'AnimTicksPerSecond' (Name)? '{' (UUID)? AnimTicksPerSecondValue '}' /,
  AnimTicksPerSecondValue: / $ANY /,
 
  Boolean:              / BooleanLong | ( BooleanValue ';') /,
  BooleanLong:          / 'Boolean' (Name)? '{' (UUID)? BooleanValue '}' /,
  BooleanValue:         / $ANY /,
 
  Boolean2d:            / Boolean2dLong | ( Boolean2dValue ';') /,
  Boolean2dLong:        / 'Boolean2d' (Name)? '{' (UUID)? Boolean2dValue '}' /,
  Boolean2dValue:       / $ANY /,
 
  ColorRGB:             / ColorRGBLong | (ColorRGBValue ';') /,
  ColorRGBLong:         / 'ColorRGB' (Name)? '{' (UUID)? ColorRGBValue '}' /,
  ColorRGBValue:        / FloatValue:red FloatValue:green FloatValue:blue /,
 
  ColorRGBA:            / ColorRGBALong | (ColorRGBAValue ';') /,
  ColorRGBALong:        / 'ColorRGBA' (Name)? '{' (UUID)? ColorRGBAValue '}' /,
  ColorRGBAValue:       / FloatValue:red FloatValue:green FloatValue:blue FloatValue:alpha /,
 
  Coords2d:             / Coords2dLong | (Coords2dValue ';') /,
  Coords2dLong:         / 'Coords2d' (Name)? '{' (UUID)? Coords2dValue '}' /,
  Coords2dArray:        / Coords2dValue (',' Coords2dValue)* (',')? ';' /,
  Coords2dValue:        / FloatValue:u FloatValue:v /,
(中略)
  XSkinMeshHeader:      / XSkinMeshHeaderLong | ( XSkinMeshHeaderValue ';') /,
  XSkinMeshHeaderLong:  / 'XSkinMeshHeader' (Name)? '{' (UUID)? XSkinMeshHeaderValue '}' /,
  XSkinMeshHeaderValue: / $ANY /,
}, "XFile", Gin.SPACE);

function XFileHandler() { this._s = [] };
XFileHandler.prototype = {
  first: function(v){ return v[0]; },
  header: function(v){ alert("version: " + v[1]); },
  identifier: function(v){ alert("identifier:" + v[0]); },
  xof: function(v){ alert("xof"); },
  showValue: function(v){ alert("value:" + v); },
  name: function(v){ alert("name:" + v); },
(中略)
  // Mesh
  mesh:      function(v){ alert("Mesh start"); },
  nVertices: function(v){ alert("nVertices: " + v); },
  vertices:  function(v){},
  nFaces:    function(v){ alert("nFaces: " + v); },
  faces:     function(v){},
(中略)
};
 
var match;
function parseXFile(request)
{
  var text = request.responseText;
  var handler = new XFileHandler();
  match = xfileGrammar.parse(text, handler);
  if(match && match.full) {
    alert("OK");
  }else{
     alert("NG");
  }
}

var url = "xfile.x";
var myAjax = new Ajax.Request(
  url,
  {
    method: 'get',
    onComplete: parseXFile
  });
}
パースしたいデータの構文が分かっているなら、それをそのまま書いてやれば簡単にパーサが実装できる。
速度を優先するなら、各ファイル形式に合わせて自分で実装すべし。
xファイルのパーサはこんな感じで作りました。
var XParser = Class.create({
  _text: null,
(中略)
  parse: function(text){
    this._text = text;
    if(!this._obj){
      this._obj = new XModel();
    }
    this._offset = 0;
    this._err = 0;

    if(!this.XFileHeader(this._obj)){
      alert("header format error");
      this._err = 1;
      return null;
    }
    while(this.XObjectLong(this._obj)){}
    if(this._err){
      alert("obj format error:" + this._err);
      return null;
    }
    this.splitFaceNormals();

    return this._obj;
  },

  moveIndex: function(len) {
    this._text = this._text.substring(len);
    this._offset += len;
  },

  getString: function(pattern) {
    this.skip();
    var str = this._text.match(pattern);
    if(str != null){
      this.moveIndex(str[0].length);
      return str[0];
    }
    return null;
  },

  /* matching patterns */
  skip: function() {
    var i=0;
    var code;
    while(1){
      code = this._text.charCodeAt(i);
      if(code != 32 && (code < 9 || code > 13)){
        break;
      }
      i++;
    }
    if(i>0){
      this.moveIndex(i);
    }
  },

  _integerPattern: new RegExp(/^(-|\+)?\d+;?/),
  getInteger: function() {
    var str = this.getString(this._integerPattern);
    var val = parseInt(str);
    return val;
  },

  _floatPattern: new RegExp(/^(-|\+)?(\d)*\.(\d)*;?/),
  getFloat: function() {
    var str = this.getString(this._floatPattern);
    var val = parseFloat(str);
    return val;
  },
(中略)
  XObjectLong: function(parent){
    var id = this.getWord();
    if(id == null){
      return null;
    }
    switch(id){
      case "template":
        return this.Template(parent);
      case "Header":
        return this.Header(parent);
      case "Mesh":
        return this.Mesh(parent);
      case "MeshMaterialList":
        return this.MeshMaterialList(parent);
      case "MeshNormals":
        return this.MeshNormals(parent);
      case "MeshTextureCoords":
        return this.MeshTextureCoords(parent);
      case "MeshVertexColors":
        return this.MeshVertexColors(parent);

      default:
        alert("unknown type:" + id);
        break;
    }
    return false;
  },
(中略)
  Mesh: function(parent) {
    this.getLeftBrace();

    // vertices
    var nVertices = this.getInteger();
    var skinArray = $A();
    for(var i=0; i<nVertices; i++){
      var skin = new Skin();
      var pos = new DHVector3();
      pos.x = this.getFloat();
      pos.y = this.getFloat();
      pos.z = -this.getFloat();
      skin.position = pos;

      skin.boneIndex[0] = 0;
      skin.boneIndex[1] = -1;
      skin.boneIndex[2] = -1;
      skin.boneIndex[3] = -1;

      skin.skinWeight[0] = 1;
      skin.skinWeight[1] = 0;
      skin.skinWeight[2] = 0;
      skin.skinWeight[3] = 0;

      skinArray.push(skin);

      this.getCommaOrSemicolon();
    }
    this._obj.skinArray = skinArray;
    this._obj.dynamicSkinOffset = -1;

    // faces
    var nFaces = this.getInteger();
    var faces = $A();
    for(var i=0; i<nFaces; i++){
      var face = this.getIntegerArray();
      faces.push(face);
    }
    this._indices = faces;
    this.getRightBrace();

    return true;
  },
(中略)
}

2012/10/05(Fri) 01:21:39

 静的ファイルのキャッシュ無効化(JavaScriptの場合) / magicien 

ウェブサーバ上でプログラムからtxtファイルを頻繁に更新しているような場合、ウェブブラウザでキャッシュ機能が有効になっていると(というか大抵なっているので)、ローカルにあるキャッシュを見に行ってしまい、サーバ上の最新のtxtファイルを見に来てくれない、という場合の対応。

時と場合によりやり方はいろいろあるんだろうけど、JavaScriptでは次のようなソースを書いた。
var url = "dynamic_content.txt";
url += "?" + (new Date() * 1); // disable cache

var data;
new Ajax.Request(fileName, {
  method: 'get',
  asynchronous: false,
  onComplete: function(result){
    data = result.responseText;
  },
});
URLの後ろに時刻(ミリ秒)をクエリで追加してやると、あたかも違うファイルを呼び出しているように見えるので、毎回サーバまで読み込みに行くようになる。
上記の例だと、urlの中身は "dynamic_content.txt?1349282126489" のようになる。
new Date()のままだと、"Thu Oct 04 2012 01:35:26 GMT+0900 (JST)" のような文字列になるため、適当に数値計算をして、整数にキャストする。ただ、"new Date() + 0" みたいなことをすると文字列が連結され、"Thu Oct 04 2012 01:35:26 GMT+0900 (JST)0" となってしまうため、0を引くなり、1を掛けるなりしてやればOK。
まぁクエリが日付のままでも問題無いとは思いますが。
2012/10/04(Thu) 01:44:24

 Macのsayコマンド / magicien 

昨日紹介した動画中の「ファーストステージ」とか「パーフェクト」とかいう声は、Macのsayコマンドを使っています。

Macには元々スピーチ機能というのが付いていて、sayコマンドを使って簡単に喋らせることができます。
$ say 1st stage
こんなコマンドを実行すると、テキストを読み上げてくれます。
-vオプションで声の種類を選択することが可能で、I.Q REVENGEで使ったのは、Alexという声。
say -v Whisper でゴノレゴの声が!
読み上げの速度はコマンドのオプションからは指定できないのですが、システム環境設定の「スピーチ」から読み上げ速度の設定が可能で、設定した内容はsayコマンドの出力結果にも反映されます。

残念ながら日本語のテキストは読んでくれないので、ローマ字で頑張って書かなければいけません。また、英語の発音もそれなりだったり。"Perfect" のアクセントとか...
最初はミクさんにExcellent!としゃべらせようと悪戦苦闘していましたが、よくよく考えたらもっとそれっぽいのがMacに付いてるじゃん、ということでこちらにしました。
キューブに踏みつぶされたり、ゲームオーバのときは音量小さいですが、ミクさんの声を使ってます。
追記:Lionだと日本語も読んでくれるらしい。なん...だと...
2012/10/03(Wed) 01:35:57

 【MMD】ブラウザで踊ってもらった(その7)【WebGL】 / magicien 

先日、こっそりサンプルを公開したら、本当に誰も遊びに来てくれなかったので、紹介動画を作りました。


奇しくも、あれからちょうど半年。嘘が真になる裏エイプリルフールというやつです。
年内にその8を公開したいけど、ネタが無いなぁ。

2012/10/01(Mon) 23:52:15

 ビブリア古書堂の事件手帖―栞子さんと奇妙な客人たち / magicien 

ビブリア古書堂の事件手帖―栞子さんと奇妙な客人たち (メディアワークス文庫) 夏休み中、読もうと思って買った10冊の小説のうち、読み終わったのは2冊だけ... この本はそのうちの1冊。

売れに売れている本を紹介するのもあまり意味が無い気がしますが、自分の読書記録を兼ねて。
タイトルから分かるとおり、古書店を舞台にしたミステリー。
ミステリーといえば探偵役が必要なわけで、この作品では、表紙にもなっている栞子さんが探偵役、主人公でニートの危機にさらされている五浦大輔がワトソン役となっています。
実在する本が各話のタイトルになっているのが特徴で、それらの本をテーマにした事件やら何やらがあり、栞子さんが本の知識とミス・マープル顔負けの洞察力で解決していく、という話。

読んで思ったのは、シリーズ向けのうまい設定だなぁというのと、話の展開が割と早い段階で読めてしまう、ということ。
それだけの材料を読者に与えるフェアなミステリーとも言えますし、重要なのは個々の話ではなく、シリーズを通して各話の裏を流れる本流ということかもしれません。
また、注目すべきは栞子さんの本に対する膨大な知識で、つまりは著者の三上さんの知識なわけで、その深さと広さには、もう参りましたという感じです。1巻の話の一つがヴィノグラードフ・クジミン「論理学入門」だったり、2巻では漫画が出てきたり、守備範囲どれだけ広いんですか。小説家が実在の小説を題材にするのは、かなりのプレッシャーがあったんじゃないかと上から目線で勝手に想像していますが、そんなプレッシャーを感じさせない、小説として純粋に楽しめる作品じゃないかと思います。

ちなみに、今2巻まで読み終わったところですが、どちらも大好物な「余韻の残るハッピーエンド」でした。
2012/09/30(Sun) 21:35:11

 JavaScriptでバイナリデータの読み込み / magicien 

JavaScriptでPMD、VMD等のバイナリデータを読み込んだときに使った方法。

HTML5のAPIの実装が進めば、こんなことする必要も無くなる気がしますが、今使っている方法は次のとおり。

まずはファイルをバイナリとして取得する必要がある。
読み込みの対象がFileオブジェクトの場合は、FileReaderを使えばOK。
function readData(data) {
 ...
}

function readBinaryFile(file) {
  var reader = new FileReader();
  reader.onloadend = function() {
    readData(reader.result);
  };
  reader.readAsBinaryString(file);
}
読み込み対象がサーバ上のファイルであれば、"charset=x-user-defined" として読み込む。
例によって prototype.js を使っているので、Ajax.Requestのサブクラスを作って次のようにした。
var BinaryRequest = Class.create(Ajax.Request,
  initialize: function($super, url, options) {
    $super(url, options);
  },

  request: function() {
    this.transport.overrideMimeType('text/plain; charset=x-user-defined');
    Ajax.Request.prototype.request.apply(this, argument);
  },
});

function readBinaryFileFromURL(url) {
  new BinaryRequest(url, {
    method: 'GET',
    onComplete: function(response) {
      readData(response.responseText);
    },
  });
}
これで、データを読み込むところまでは出来たので、あとはデータの解析をするだけ。
とはいえ、そっちの方が大変ですが。データの解析はまた今度。
2012/09/29(Sat) 01:06:13

 MacでのWeb用音声変換 / magicien 

ウェブ用にmp3とoggとwavを用意するときに毎回分からなくなるのでメモ。

・wav→mp3
こういうときに限ってQuickTimeが使えないので、iTunesを使う。
「iTunes」→「環境設定…」→「一般」→「CDをセットしたときの動作」の横の「読み込み設定」→「読み込み方法」で「MP3エンコーダ」を選択
その後、読み込んだ曲を右クリック(Ctrl+クリック)で選択、「MP3バージョンを作成」で出来上がり。

・mp3→wav
上と同じ。「読み込み方法」で「WAVエンコーダ」を選択
その後、曲を右クリック(Ctrl+クリック)で選択、「WAVバージョンを作成」で出来上がり。

・wav→ogg
oggencコマンドを使う。
FinkとかMacPortsとかで vorbis-tools をインストールすると、使えるっぽい。
$ oggenc -o output.ogg input.wav
これでOggファイルの出来上がり。

・ogg→wav
oggdecコマンドを使う。
同じく vorbis-tools でインストールされる。
$ oggdec -o output.wav input.ogg
mp3とoggの変換は一度wavにしてから変換すれば良いんじゃないかと思う。
あと、音声素材サイトでogg形式のファイルが公開されていると、おっ!と思う。
こことかおすすめです。

2012/09/28(Fri) 00:31:18

 Operaで "instanceof Image" がエラーになる / magicien 

Operaで次のようなコードを実行したところ、"Unhandled Error: Second argument to 'instanceof' does not implement" というエラーになった。
var img = new Image();
if(img instanceof Image) {
  alert("img is an instance of Image!");
}
なぜかImageにはinstanceofが使えないらしい。
Operaで上のような判定をしたい場合は、Imageの代わりにHTMLImageElementを使えばOK。

2012/09/27(Thu) 01:54:00