2011-07-01 16 views
5

[注:提出する前にこれを読み返すと、私はこのQが叙事詩のビットになったことを認識しました。この追求の背後にある理由について私が長い説明をしてくれてありがとう、ありがとう。私は同様のプロジェクトを進める別の人を助ける立場にあったと思うので、質問の背後にある動機を知っていればもっと上に乗ることができます。]最近、これは、Eisenscriptと呼ばれる(ほとんど)文脈自由文法から3Dジオメトリを生成するためのツールです。構造SynthはContext Free Artに触発されています。文脈自由文法は、驚くほど単純なルールセットから素晴らしい結果を生み出すことができます。Pythonで内部DSLとして文脈自由な設計文法を表現するには?

私の現在のStructure Synthワークフローでは、Structure SynthからOBJファイルをエクスポートし、Blenderにインポートし、ライト、マテリアルなどを設定してから、Luxrenderでレンダリングします。残念なことに、これらのOBJファイルをインポートすると、複雑なジオメトリを持つ数千ものオブジェクトが存在することがあるため、Blenderが頻繁に停止することがあります。 Structure Synthは基本的な形だけを生成するので、私は 'かなり'と言っていますが、三角形で表現された球はまだ多くの面を持っています。

したがって、Blenderで構造を直接生成する方が、現在のプロセスよりも望ましいでしょう(BlenderのPythonスクリプティングの深いサポートがこれを可能にするはずです)。インテリジェントなPythonライブラリは、Blenderのインスタンシング能力を使用して、1つのメッシュを使用して無数のオブジェクトを生成し、メモリを節約できます。 Plus Blenderはフル機能の3Dスイートで、CFDGを解釈する能力は、Structure Synthが提供できるものをはるかに超える創造的な可能性を提供します。

私の質問は、Eisenscript文法をPython DSLに変換するにはどのように最善の方法です。ここでは簡単なEisenscriptは次のようになります。説明するために

set maxdepth 2000 
{ a 0.9 hue 30 } R1 

rule R1 { 
    { x 1 rz 3 ry 5 } R1 
    { s 1 1 0.1 sat 0.9 } box 
} 

rule R1 { 
    { x 1 rz -3 ry 5 } R1 
    { s 1 1 0.1 } box 
} 

、R1への最初の呼び出し(2行目)は、ランダムにR1の二つの定義のいずれかを呼び出します。 R1の各定義は、R1を(再帰的に)呼び出し(2つの定義のうちの1つをランダムに呼び出す)、ボックスを作成します。再帰が2000年の深さに達した後、最初の行は世代を殺します。

Jeremy Ashkenas(CoffeeScriptの名声)は、ブロックを使用してcontext free DSL in Rubyを実装しました。内部的には、各ルール 'name'のハッシュキーを作成し、そのルールの各定義のブロックを配列に格納します。ルールが呼び出されるときにランダムに選択されます。

以前Eisenscriptルールの定義がそうのようなRubyのDSLに変換します:

rule :r1 do 
    r1 :x => 1, :rz => 3, :ry => 5 
    box :s => [1, 1, 0.1], :sat => 0.9 
end 

rule :r1 do 
    r1 :x => 1, :rz => -3, :ry => 5 
    box :s => [1, 1, 0.1] 
end 

私は初心者のPythonのユーザーだとそうPythonの関数型プログラミング機能のいくつかの研究を行ってきました。ラムダはあまりにもJeremyのRuby DSLに似たものを作成するにはあまりにも制限されているようですが、ラムダは匿名関数の唯一のオプションです

経験豊富なPythonistaがデザインにどのようにアプローチするのでしょうか?

答えて

3

RubyライブラリのブロックをContextFreeインスタンスに渡され、無限ループを回避するための条件が追加され、インスタンスメソッドとしてインスタンスに追加される事前定義された関数に変換することによって、初期プロトタイプを構築することにしました。ここに現在の状態があります。批判は歓迎です。これは私の最初のPythonコードの一部です。私はRubyの慣用句をPythonの慣用句に戻したいと思っています。

import random 

class ContextFree(object): 
    def __init__(self): 
    self.rules = {} 
    # grab any instancemethod to get an instance of the instancemethod class 
    self.instancemethod = type(self.add_rule) 
    self.max_depth = 100 
    self.depth = 0 

    def add_rule(self, func, prob=1): 
    rule_name = func.__name__ 

    if not rule_name in self.rules: 
     self.rules[rule_name] = { 'funcs' : [], 'total' : 0 } 

    total = self.rules[rule_name]['total'] 
    self.rules[rule_name]['funcs'].append([range(total,(prob+total)), func]) 
    self.rules[rule_name]['total'] += prob 

    def augmented_func(self, options={}): 
     if not self.depth >= self.max_depth: 
     self.depth += 1 
     pick = self.determine_rule(rule_name) 
     print('Generation', self.depth) 
     pick(self) 

    self.__dict__[rule_name] = self.instancemethod(augmented_func, self) 

    def determine_rule(self, rule_name): 
    rule = self.rules[rule_name] 
    winning_number = random.randrange(0, self.rules[rule_name]['total']) 
    for func in rule['funcs']: 
     if winning_number in func[0]: 
     return func[1] 

cf = ContextFree() 

def box(self): 
    print('Rule for box1') 
    self.box() 

cf.add_rule(box) 

def box(self): 
    print('Rule for box2') 
    self.box() 

cf.add_rule(box) 

cf.box() 

# Output: 
## Generation 1 
## Rule for box2 
## Generation 2 
## Rule for box2 
## Generation 3 
## Rule for box1 
## Generation 4 
## Rule for box2 
## Generation 5 
## Rule for box1 
## Generation 6 
## Rule for box2 
## Generation 7 
## Rule for box2 
## Generation 8 
## Rule for box1 
## Generation 9 
## Rule for box2 
## Generation 10 
## Rule for box2 
## Generation 11 
## Rule for box1 
## Generation 12 
## Rule for box1 
## Generation 13 
## Rule for box1 
## Generation 14 
## Rule for box1 
## Generation 15 
## Rule for box2 
## Generation 16 
## Rule for box1 
## Generation 17 
## Rule for box1 
## Generation 18 
## Rule for box1 
## Generation 19 
## Rule for box1 
## Generation 20 
## Rule for box1 
## Generation 21 
## Rule for box2 
## Generation 22 
## Rule for box2 
## Generation 23 
## Rule for box1 
## Generation 24 
## Rule for box2 
## Generation 25 
## Rule for box1 
## Generation 26 
## Rule for box2 
## Generation 27 
## Rule for box2 
## Generation 28 
## Rule for box1 
## Generation 29 
## Rule for box2 
## Generation 30 
## Rule for box2 
## Generation 31 
## Rule for box2 
## Generation 32 
## Rule for box2 
## Generation 33 
## Rule for box2 
## Generation 34 
## Rule for box1 
## Generation 35 
## Rule for box2 
## Generation 36 
## Rule for box1 
## Generation 37 
## Rule for box1 
## Generation 38 
## Rule for box1 
## Generation 39 
## Rule for box1 
## Generation 40 
## Rule for box2 
## Generation 41 
## Rule for box1 
## Generation 42 
## Rule for box1 
## Generation 43 
## Rule for box1 
## Generation 44 
## Rule for box1 
## Generation 45 
## Rule for box2 
## Generation 46 
## Rule for box1 
## Generation 47 
## Rule for box2 
## Generation 48 
## Rule for box1 
## Generation 49 
## Rule for box2 
## Generation 50 
## Rule for box1 
## Generation 51 
## Rule for box1 
## Generation 52 
## Rule for box1 
## Generation 53 
## Rule for box2 
## Generation 54 
## Rule for box2 
## Generation 55 
## Rule for box2 
## Generation 56 
## Rule for box2 
## Generation 57 
## Rule for box2 
## Generation 58 
## Rule for box1 
## Generation 59 
## Rule for box1 
## Generation 60 
## Rule for box1 
## Generation 61 
## Rule for box2 
## Generation 62 
## Rule for box2 
## Generation 63 
## Rule for box2 
## Generation 64 
## Rule for box1 
## Generation 65 
## Rule for box2 
## Generation 66 
## Rule for box2 
## Generation 67 
## Rule for box2 
## Generation 68 
## Rule for box2 
## Generation 69 
## Rule for box2 
## Generation 70 
## Rule for box1 
## Generation 71 
## Rule for box2 
## Generation 72 
## Rule for box2 
## Generation 73 
## Rule for box2 
## Generation 74 
## Rule for box1 
## Generation 75 
## Rule for box2 
## Generation 76 
## Rule for box1 
## Generation 77 
## Rule for box1 
## Generation 78 
## Rule for box2 
## Generation 79 
## Rule for box1 
## Generation 80 
## Rule for box2 
## Generation 81 
## Rule for box1 
## Generation 82 
## Rule for box1 
## Generation 83 
## Rule for box1 
## Generation 84 
## Rule for box1 
## Generation 85 
## Rule for box2 
## Generation 86 
## Rule for box1 
## Generation 87 
## Rule for box1 
## Generation 88 
## Rule for box2 
## Generation 89 
## Rule for box2 
## Generation 90 
## Rule for box1 
## Generation 91 
## Rule for box1 
## Generation 92 
## Rule for box1 
## Generation 93 
## Rule for box1 
## Generation 94 
## Rule for box1 
## Generation 95 
## Rule for box1 
## Generation 96 
## Rule for box2 
## Generation 97 
## Rule for box1 
## Generation 98 
## Rule for box2 
## Generation 99 
## Rule for box1 
## Generation 100 
## Rule for box2 
+1

'self.instancemethod'の代わりに' types.MethodType'を使うことができます。また '__dict__'を直接変更するのではなく、' setattr(obj、name、thing) 'を使うべきです。 – JBernardo

4

文脈自由文法のパーサーを書くのは難しいです。あなたはおそらく自分で物事をより簡単にするために何らかの種類のライブラリを使うほうが良いでしょう。

私はPyParsingモジュールをチェックアウトします。このダウンロードにはいくつかの例がありますが、そのうちの1つは簡単なSQLパーサーであり、少なくとも最初のステップとして見てみると賢明かもしれません。

+0

回答ありがとう!私は実際にEisenscriptを直接解析するためのパーサを書くのではなく、内部DSLを使ってこの機能を実装することを好みます。私は、与えられた目的のために1つを使うことは、無料でホスト言語のすべての能力を提供するという事実のために、可能な限り、内部DSLを好むようになった。 –

+0

私はあなたが何を意味しているのか理解しています。 FWIWでは、pyparsingはDSLを実装するためのかなり良いツールです。 http://fmeyer.org/en/writing-a-DSL-with-python.htmlとそのディスカッションについては、http://news.ycombinator.com/item?idをご覧ください。 = 808091。 – Wilduck

+0

私はそれを認識しませんでした。私はそれらの記事に時間を費やします。再度、感謝します。 –

関連する問題