DTPab

印刷やデザイン、アドビ製アプリやスクリプトなど、雑多な技術ブログ

ExtendScriptの単位付き計算

【追記 5/28 15:00】
重大な不具合をあるふぁ(仮)さんが見つけてくださったのと、最初に書いたスクリプト(単位付き計算を行う関数)が加算以外に対応していなかったことに気づいたのでここに追記して訂正いたします。
まずあるふぁ(仮)さんからのありがたいご指摘。

UnitValue、なんと雑な仕様なのでしょう…。泣けてきます。
UnitValueを安易に数値と比較しないほうがいいようです。
当然ですが、parseIntparseFloatでUnitValueオブジェクトを数値型に変換したり、UnitValueオブジェクトからvalueプロパティを参照すればちゃんとした数値型です。最悪このあたりを使うか、ちゃんと比較するならasメソッドでちゃんと数値に換算した上で比較したほうがいいです。

続いてスクリプトの改訂版です。最初にアップしたものは「加算」しかできなかったので大幅に修正しました。

function (formula, as){
    var regexp = /([0-9.]+)([a-z]+)*/ig;
    //UnitValueを利用する関数
    var myUnit = function (value, unit){
        if (!unit) unit = as;
        return new UnitValue(parseFloat(value), unit).as(unit);
    }
    formula = formula.replace(/ +/g, ""); //スペースが入っていたら削除
    var newFormula = formula; //最終的な置換結果用の計算式
    //find[1]:数値、find[2]:単位
    while(find = regexp.exec(formula)){
        var calc = myUnit(find[1], find[2]).toString(); //replaceメソッドで使うので文字列にしている
        newFormula = newFormula.replace(find[0], calc);
    }
    //evalメソッドで計算
    return eval(newFormula);
}

これ以降のこの記事は、この追記の内容を踏まえる前のものです。一部正確でない表現や勘違いがありますが、一応そのまま残しています。
ただスクリプトに関しては完全に漏れがあったのと、混同されてしまうと問題なので、修正前のスクリプトは削除しました。
訂正してお詫びいたします。

【追記 5/28 1:10】
@AJABON先生より、UnitValueでは誤差が出ることがあるかも?とご助言いただきました。
もともとJavaScriptでは小数を含む計算が苦手なこともあり*1、念のため多少の誤差が出るかもしれないという認識のもとお使いください。

ただ、UnitValueの誤差がどれほどかを試そうとしたところ、意外な発見がありました。

var result = UnitValue(20.2, "pt") - UnitValue(20, "pt");
$.writeln(result);
$.writeln(0.2 === result); //true

var result2 = 20.2 - 20;
$.writeln(result2);
$.writeln(0.2 === result2); //false

UnitValueを使うと、この計算式がtrueになったのです…。単位が付いているということでなにかご利益があるのか?w
いずれにせよ、小数の計算に役立つとは思いませんでした。とはいえ相手はJavaScriptなので、過信しないほうがいいでしょうね。
--追記ここまで--

ちょっと前に、ExtendScriptで「単位が入り混じった計算を行うスクリプト」を紹介しました。

せっかくなので、もう少し実用性のあるコードも用意してみました。単位が入り混じった計算を行うスクリプトです。

JavaScriptの検索とInDesignの検索 - DTPab

が! もっといい方法がありました。

UnitValueオブジェクトを利用する

たまたま調べ物をしていて見つけたもので、とっても便利なものでした。ほんと、誰か教えてこういうの…。

var v = new UnitValue (100, "pt");

こんな感じで値と単位を引数にして渡します。任意の単位にして値を取り出すことが可能で、

$.writeln(v.as("mm"));

UnitValue.asメソッドを利用すると、「100pt」として定義した変数vを、簡単にmm単位に換算できます。

めっちゃ便利じゃん。

単位が入り混じった計算を行うスクリプト

改めて書いてみました。第一引数に文字列として計算式を渡し、第二引数に受け取りたい単位を渡します。

※冒頭のスクリプトを参照してください

単位を付けずに計算式に記述した場合は、第二引数の単位に合わせる仕様です。こんなにスッキリするとは…。
使い方はこんな感じ。

$.writeln(calculateWithUnit("10mm + 1.5pt + 2inch + 3pica + 2.5", "mm")); //76.5291666666667

UnitValueのプロパティとメソッド

プロパティ

プロパティ 説明
baseUnit 計算の基準となる値がUnitValueオブジェクトで入っています。おそらく定数で、0.01388888888889。単位はinch。
type そのUnitValueオブジェクトの単位
value そのUnitValueオブジェクトの値

メソッド

メソッド 引数 説明
as UnitName (文字列) 単位表記。入っている値をどの単位で換算して取り出すか。
convert UnitName (文字列) その単位で換算ができるか。できる場合はtrue。できなければエラー(ぇ

ちなみに、ESTKのオブジェクトモデルビューアで確認する場合は「Core JavaScript Classes」ブラウザにあります。

*1:例えば中綴さんの『小数を含む計算で起こる演算誤差』などがわかりやすいです。