3

この形式のパス定義があります (例):

<path d="M 20 30 L 20 20 20 40 40 40"/>

これは、Lua では次のようになります。

"M 20 30 L 20 20 20 40 40 40"

純粋なLuaでそれを解析して次のようなものを取得するにはどうすればよいですか:

{'M', 20, 30, 'L', 20, 20, 20, 40, 40, 40 }

または、完全に:

{{'M', 20, 30}, {'L', 20, 20}, {'L', 20, 40}, {'L', 40, 40}}

Lua パターンにはそのような機能がありますか?

編集:すべての有効な SVG パス、または少なくとも Inkscape で生成されたパスをカバーしたいと考えています。 仕様 inkscape生成パス

4

3 に答える 3

3

もちろん、直接ではなく、単純化されたパーサーが必要になります。

好奇心に負けたけど、普段は「Do this work for me」の投稿は嫌い

--- Parse svg `path` attribute into 2-D array
function parsePath(input)
    local output, line = {}, {};
    output[#output+1] = line;

    input = input:gsub("([^%s,;])([%a])", "%1 %2"); -- Convert "100D" to "100 D"
    input = input:gsub("([%a])([^%s,;])", "%1 %2"); -- Convert "D100" to "D 100"
    for v in input:gmatch("([^%s,;]+)") do
        if not tonumber(v) and #line > 0 then
            line = {};
            output[#output+1] = line;
        end
        line[#line+1] = v;
    end
    return output;
end

-- Test output
local input = 'M20 30L20 20,20 40;40 40 X1 2 3 12.8z';
local r = parsePath(input);
for i=1, #r do
    print("{ "..table.concat(r[i], ", ").." }");
end

Inkscape は常に命令と数字の間にスペースを入れるように見えるため、Inkscape によって生成されたファイルのみを解析する場合は、2 つの gsub 行を省略できます。

この関数は、Inkscape がパス定義に入れたいランダムな文字のほとんどを破棄しますが、標準に準拠するすべてのパス定義を本当に読みたい場合は、詳細を解決する必要があるかもしれません。

更新 ( SVG BNF 定義をざっと調べた後)

SVG 標準Superfluous white space and separators such as commas can be eliminatedには と記載されていますが、BNF 表記を見てみると、空白とコンマ以外の区切り文字が見つかりませんでした。

したがって、おそらく 2 番目の正規表現を に変更できます"([^%a%d%.eE-]+)"。しかし、次の関数の方がはるかに適していると思いました。

function parsePath(input)
    local out = {};

    for instr, vals in input:gmatch("([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)") do
        local line = { instr };
        for v in vals:gmatch("([+-]?[%deE.]+)") do
            line[#line+1] = v;
        end
        out[#out+1] = line;
    end
    return out;
end

-- Test output
local input = 'M20-30L20,20,20X40,40-40H1,2E1.7 1.8e22,3,12.8z';
local r = parsePath(input);
for i=1, #r do
    print("{ "..table.concat(r[i], ", ").." }");
end

eこの関数は、不必要な空白を省略できるという点で非常に寛大であり、 orでない最初の文字の前のデータを破棄する以外のセマンティクスを検証しませんE

また、一致しないデータは黙って無視します。

既存の命令のみを一致させたい場合は、パターン([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)を に置き換えることができます([MmZzLlHhVvCcSsQqTtAa])([^MmZzLlHhVvCcSsQqTtAa]*)。ただし、これにより、存在しない命令のすべての値が前の命令に追加されるため、これは良い考えではないと思います。スーパーセットを解析し、後でセマンティクスにエラーをスローすることをお勧めします。

于 2013-06-02T15:18:43.847 に答える
2
local path = 'M 20 30 L 20 20 20 40 40 40'

local s, t = '', {}
for c, x, y in path:gmatch'(%a?)%s*(%d+)%s*(%d+)' do
   s = (s..c):sub(-1)
   t[#t+1] = {s, tonumber(x), tonumber(y)}
end
-- Now t == {{'M', 20, 30}, {'L', 20, 20}, {'L', 20, 40}, {'L', 40, 40}}
于 2013-05-31T18:44:51.857 に答える