LineToセグメントのたくさんの部分からなるフリーハンドのユーザーが描いたSVGパスをよりスムーズなものに変換するソリューションを探しています。フリーハンドで描かれたSVGパスを滑らかにする方法は?
希望言語はJavaScriptですが、アドバイスも歓迎します。
LineToセグメントのたくさんの部分からなるフリーハンドのユーザーが描いたSVGパスをよりスムーズなものに変換するソリューションを探しています。フリーハンドで描かれたSVGパスを滑らかにする方法は?
希望言語はJavaScriptですが、アドバイスも歓迎します。
まず、raphaelなどの優れたグラフィックスライブラリを使用することをおすすめします。実際にjavascriptを使用して図面を実行するプロセスを簡略化します。
スムージングの非常に単純な方法は、すべてのlinetoコマンドを同等のcurvetoコマンドで変換し、各ラインセグメントの角度に基づいていくつかの制御点を計算することです。例えば、
<svg width="1000" height="1000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<path d="
M250 150
L150 350
L350 350
L250 150
" />
</svg>
は、これらの両方は正三角形次のステップでは、制御点の位置を計算することであろう
を描画する
<svg width="1000" height="1000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<path d="
M250 150
C250 150 150 350 150 350
C150 350 350 350 350 350
C350 350 250 150 250 150
" />
</svg>
となります。一般に、滑らかなコーナーの両側のコントロールポイントは、頂点を通過する仮想線に当たるようにします。正三角形の頂点の場合、これは水平線です。
<svg width="1000" height="1000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<path d="
M250 150
C230 150 140 333 150 350
C160 367 340 367 350 350
C360 333 270 150 250 150
" />
</svg>
トリッキーな部分は、コントロールポイントを計算しているが、それは、単純な三角の問題以下、はるかに変わる:いくつかの操作をした後、あなたはこのような何かを得ることができます。前述したように、ここでの目標は、コーナー頂点を2等分する線上に2つのコントロールポイントを配置することです。我々は2つの線分があると、例えば、:
A. (0,0) to (3,2)
B. (0,0) to (1,-4)
the absolute angle of A is arctan(2/3) = 33.69 deg
the absolute angle of B is arctan(-4/1) = -75.96 deg
the bisection angle of AB is (33.69 + -75.96)/2 = -21.135
the tangent angle is AB is (-21.135 + 90) = 68.865
接線角度を知ることを、我々は制御点位置を算出することができる
smoothness = radius = r
tangent angle = T
Vertex X = Xv
Vertex Y = Yv
Control Point 1:
Xcp1 = cos(T)*r
Ycp1 = sin(T)*r
Control Point 2:
Xcp2 = cos(T)*(-r)
Ycp2 = sin(T)*(-r)
最後の問題は、実際curvetoといった各制御点を配置する場所でありますコマンド:
CX1 Y1 X2 Y2 X3 Y3
X3とY3が頂点の位置を定義します。 X1 Y1とX2 Y2は制御点を定義する。 X1 Y1は頂点を入力する方法のベクトルを定義し、X2 Y2は離れる方法のベクトルを定義すると考えることができます。今、あなたは、2つの制御点を持っていることを、あなたは、これは重要な決定である
CXcp1 Ycp1 Xcp2 Ycp2 0 0
または
CXcp2 Ycp2 Xcp1 Ycp1 0 0
に決定する必要があります。それらを後ろに持っていくと、形はループのように見えます。この時点で、この決定がどのように行われるべきかを決めることができるはずです...
また、これは非常に簡単な解決策ですが、手描きのパスによく見える傾向があります。より良い解決策は、それをさらに進め、交差点を内側の各線分交差点の凹部に向かって移動させることができる。これはかなり困難です。
コントロールポイントの計算方法の例を教えてください。 – florianguenther
私はこの道を手伝ってくれる数学を追加しました – jordancpaul
これはおそらく私が見た最高の答えの1つです – austinbv
私はその背後にあるアルゴリズムを潜んでいる、私は彼らがpath simplificationのための一つの例を持っていることがわかりましたpaperjsの例を見て、同じ問題でよ、あなたはここでそれを見ることができます:それはパスの魔女を簡素化するアルゴリズムですhttps://github.com/paperjs/paper.js/blob/master/src/path/PathFitter.js
"An algorithm for automatically fitting digitized curves"という名前の学術研究のjs版(最適化あり)です。
私はこのアルゴリズムのみを抽出しており、おそらくsvg.jsへのプラグインとして公開しています。
はのは、ユーザの描画がタプルの配列である、我々はthis articleで
const points = [[100, 50], [50, 15], [5, 60], [10, 20], [20, 10], [30, 190], [40, 10], [50, 60], [60, 120], [70, 10], [80, 50], [90, 50], [120, 10], [150, 80], [160, 10] ]
const lineProperties = (pointA, pointB) => {
const lengthX = pointB[0] - pointA[0]
const lengthY = pointB[1] - pointA[1]
return {
length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
angle: Math.atan2(lengthY, lengthX)
}
}
const controlPointCalc = (current, previous, next, reverse) => {
const c = current
const p = previous ? previous : c
const n = next ? next : c
const smoothing = 0.2
const o = lineProperties(p, n)
const rev = reverse ? Math.PI : 0
const x = c[0] + Math.cos(o.angle + rev) * o.length * smoothing
const y = c[1] + Math.sin(o.angle + rev) * o.length * smoothing
return [x, y]
}
const svgPathRender = points => {
const d = points.reduce((acc, e, i, a) => {
if (i > 0) {
const cs = controlPointCalc(a[i - 1], a[i - 2], e)
const ce = controlPointCalc(e, a[i - 1], a[i + 1], true)
return `${acc} C ${cs[0]},${cs[1]} ${ce[0]},${ce[1]} ${e[0]},${e[1]}`
} else {
return `${acc} M ${e[0]},${e[1]}`
}
},'')
return `<path d="${d}" fill="none" stroke="black" />`
}
const svg = document.querySelector('.svg')
svg.innerHTML = svgPathRender(points)
<svg viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" class="svg">
</svg>
詳細な説明のような何かを行うことができます想像してみましょう。
曲線を使用してみてくださいhttp://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands – Gerben