質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

HLSL

HLSLは、米マイクロソフト社によって開発された Direct3D APIで使われるプロプライエタリなシェーディング言語です。

Q&A

解決済

7回答

2093閲覧

補間用の方程式で添付画像の調整が可能なものが欲しいです

XVK

総合スコア10

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

HLSL

HLSLは、米マイクロソフト社によって開発された Direct3D APIで使われるプロプライエタリなシェーディング言語です。

1グッド

0クリップ

投稿2021/05/25 08:13

編集2021/05/25 15:32

主にゲーム用のリアルタイムなアニメーション補間のために
0<=x<=1の範囲でf(0)=0,f(1)=1となり、斜め軸(y=1-x)で線対称になる式がほしいです

条件は
y=1-x上の通過点を1点指定し、
A(0,0)で傾き真上、(1,1)で傾き真右、の場合
B(0,0)で傾き真右、(1,1)で傾き真上、の場合
の2パターンの式がほしいです

画像を添付しておきます
また、計算速度に関する問題や妥協点などあれば教えていただけると助かります
(円の方程式に指数を与えるとかなり近いのですが、内側に変化させられないため断念しました)

C++での実装を想定しています

よろしくお願いいたします

イメージ説明

追記です
図では紫のグラフ(条件A)の点を掴んでいますが、これがy=xをまたぐ際に緑(条件B)の方に切り替えて使用します
またアニメーション用途のため、xは時間を表していて線形変化を緩急のある変化に変えるために使います
(例としてスケール初期値100%~終端200%として線形ではなくいきなり180%くらいまでデカくなったのちゆっくりに落ち着くといった動きなどを表現)
そのためyの範囲は0~1で収まるものを想定しています

S字型のカーブをさせるための情報は
http://marupeke296.com/TIPS_No19_interpolation.html
が参考になったのですが今回のお椀型?の仕様に合う情報が欲しいです

更に追記

下記コードにて出力した点をプロットしたものが下記画像となります
a=0.1(緑)
a=0.25(赤)
a=0.4(水)

これが傾きの要件をみたしているのかはわからないのですが、これを線形補間で使えばもう十分かな感はあります
ただ、ソースのコメントにあるように分割時の右半分のYを求める係数がわからないため中央付近が異常に荒くなっています

struct f2 { float m_x; float m_y; }; f2 outf2[20]; int numout=0; void saiki(int div, f2 v0, f2 v1, float a) { if(div){ float sax = v1.m_x - v0.m_x;//差x float say = v1.m_y - v0.m_y;//差y f2 center_vec; center_vec.m_x = v0.m_x + sax*0.5f; //xは中間 center_vec.m_y = v0.m_y + say*(1.f-a); //yは1-aでブレンド saiki(div-1, v0, center_vec, a);//左半分の再帰呼び出し outf2[numout++] = center_vec; //motomu(div-1, center_vec, v1, a);//右側分割が上手く行かない } else{ return; } } void func() { float a = 0.25f;//掴んでいる点のx座標 f2 v0 = {0.f, 0.f};//原点 f2 v1 = {a, 1.f-a};//y=x上の点 saiki(5, v0, v1, a); outf2[numout++] = v1; //ここで出したoutf2は半分だけなのであとは手作業で鏡面写し }

イメージ説明

fana👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2021/05/25 14:03 編集

赤点の位置をy=1-x上でxが0.1刻みぐらいで変化したとき(できるだけ細かい)どのような曲線がほしいか描いてみてください
XVK

2021/05/25 15:34

追記し画像を添付しました 確認の程よろしくお願いいたします
guest

回答7

0

ベストアンサー

円の方程式の指数部を変えればいいので、その考え方に基づいてやるとすれば式としては

abs(y) = (1 - (abs(x-1))^n)^(1/n)
abs(y-1) = (1 - (abs(x))^n)^(1/n)

でしょう。nの値の大小で曲がり具合を変更できます。こちらのサイトとかで、1<=n<100くらいで試してみてください(小数可)。

関数グラフオンライン作成 | 科学技術計算ツール

この方法はp-ノルムの考え方に基づきます。

Lp空間 - Wikipedia


円のように4象限分は要らないので、絶対値を外して必要な部分だけ切り出して、

-y+1 = (1 - x^n)^(1/n)→y=-(1 - x^n)^(1/n) + 1
y = (1 - (-x+1)^n)^(1/n)

とかで良さそうです。xからyを計算したい場合はこちらを使います。

見慣れた円の方程式のような形に変形すると、

x^n + (-y+1)^n = 1
(-x+1)^n + y^n = 1

です。

制御点を通る曲線に対応するnの計算方法ですが、制御点はy = 1-x上にあるので、これを代入すると、

x^n = 1/2
y^n = 1/2

ですから、log_x 0.5とlog_y 0.5(x, yを底とする対数)を計算すれば必要なnが求まります。


式を分けないで、どっちかの式にn=log_x 0.5とlog_y 0.5を与えればy=xに対して線対称の2曲線が得られるはずです。
(この場合、(x, y)=(0.8, 0.2)なら約3.11と約0.43とかになると思います)
(これで行ける理由はなんとなくわかるようなわからないような感じなので、あとで説明を思いついたら追記しておきます)
(か、どなたかコメントで書いていただけると助かります)

投稿2021/05/26 05:23

編集2021/05/26 08:14
hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

XVK

2021/05/26 05:28

ありがとうございます 早速検証してきます
XVK

2021/05/26 05:39

完璧でした!!! ありがとうございました!!!!!!
hayataka2049

2021/05/26 06:05 編集

一応の注意点ですが、制御点の座標が厳密にx=0またはx=1のときは無限大が出てくると思うので(実際に計算するとどうなるかはわかりませんが数学的にはn=∞)、上手いこと回避してください。
hayataka2049

2021/05/26 06:18 編集

(xが一定程度0 or 1に近づいたら、それ以上nを大きくしないようにする……一定程度近づいたときのnの値を拾っておかないといけませんが)
fana

2021/05/26 06:14

なにこれ楽しい!
hayataka2049

2021/05/26 06:25

なにかで近似するという考えを捨ててゆくスタイルです。
XVK

2021/05/26 06:34

さすがです。 関数化してみました #include <math.h> //control_x == 制御点x座標(0.f < control_x < 1.f) float f(float x, float control_x) { float n = (float)(log(0.5) / log(1.f - control_x)); float y = (float)pow(1.f - pow(-x+1.f, n), 1.f/n); return y; }
guest

0

傾きの条件を無視してよくて、かつ、「斜め軸(y=1-x)で線対称になる」が譲歩できる(ちょっと違ってもいい)のなら、ガンマ補正カーブの式を使うとか
(99) OpenCV #4 : ガンマ補正で画像を見やすく調整

y=xの直線に対する対称性は、ガンマ値を逆数にします (たとえば、ガンマ=2とガンマ=1/2がy=xに対して対称)

ただし、曲線の形のコントロールは、式上では点の座標ではなくガンマ値になります
曲線がここ通ってほしい点の座標からlog関数でガンマ値を計算して曲線の式を決めたら、任意のxに対するyを計算できます

投稿2021/05/26 01:48

編集2021/05/26 02:02
jbpb0

総合スコア7653

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

XVK

2021/05/26 02:23

情報ありがとうございます pow()で指数に整数や実数を用いたシンプルな変換は今までも使っていたのですが、それだと理想の変化をしてくれないため今回の投稿に至りました
fana

2021/05/26 02:39 編集

質問文内で示されている用途を考えると, 端点での傾きや,「斜め軸(y=1-x)で線対称になる」といった形状はあまり重要な要素ではないように思うので, ガンマで十分なように思えますね. (…って書いてたら,コメントがついてた.ガンマだと形状に不満があるのね)
jbpb0

2021/05/26 03:41 編集

> それだと理想の変化をしてくれない 実際のカメラやRAW現像ソフトのガンマ補正カーブは、よく知られてる数式とは違ってたりします たとえば、ニコンの現像ソフトの場合のグラフが下記Webページにありますけど、sRGBのカーブよりも、質問者さんの要望に近そうな気がします https://blog.goo.ne.jp/-odisama-/e/4793bec515c317002f39fe5c80bc5127/?cid=74d5e771635d52b175315fbc9826964b&st=0 また、ガンマとlogのハイブリッドのHLG方式のグラフが、下記Webページの(ページ右下の数字で)12ページにあります https://www.leader.co.jp/uploads/2020/08/wp2_hdr_j1_181031.pdf 以上、ご参考までに (すぐ使える数式ではないですが)
XVK

2021/05/26 03:53

情報ありがとうございます
guest

0

こんなのどうでしょうか?

python

1import numpy as np 2import matplotlib.pyplot as plt 3 4# xは0.0と1.0は含まないようにする 5dx = 0.001 6x = np.arange(dx, 1.0, dx) 7 8# 曲線を通したい点からaを計算 9xx = 0.4 10yy = 1 - xx 11a = 1 - (1 - yy) / (yy * (1 / xx - 1)) 12 13# 曲線 (aで形が変わる) 14y1 = 1 / (1 + (1 / x - 1) * (1 - a)) 15y2 = -1 / (1 + x / (1 - x) * (1 - a)) + 1 16 17plt.plot(x, y1, x, y2) 18plt.axis('square') 19plt.show()

 
【追記】 計算式を下記のように変えたら、x=0.0, 1.0も計算できます

python

1# 曲線 (aで形が変わる) 2y1 = x / (x + (1 - x) * (1 - a)) 3y2 = -1 * (1 - x) / ((1 - x) + x * (1 - a)) + 1

投稿2021/05/26 06:13

編集2021/05/26 07:19
jbpb0

総合スコア7653

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbpb0

2021/05/26 06:16

Pythonで書いたけど、動かせる環境お持ちですかね??
jbpb0

2021/05/26 07:26 編集

https://cattech-lab.com/science-tools/function-plot/ で、関数を 1 / (1 + (1 / x - 1) * (1 - a)) -1 / (1 + x / (1 - x) * (1 - a)) + 1 のように入力して、aの部分のみ0~1の範囲(1はダメ)で変えると、形が変わります 【追記】 「描画設定」で、x軸の範囲を0~1に設定してください 【追記2】 a=0でy=xの直線、aを大きくすると二つの曲線が離れます 【追記3】 関数を下記のように変えると、x=0.0, 1.0も計算できます x / (x + (1 - x) * (1 - a)) -1 * (1 - x) / ((1 - x) + x * (1 - a)) + 1
jbpb0

2021/05/26 06:31

四則演算だけなので、計算コストは低いです
XVK

2021/05/26 06:41

ありがとうございます Pythonは触ったことがないです申し訳ありません グラフ今から触ってみます
jbpb0

2021/05/30 14:07

いかがでしょうか? 計算コストが低いので、「リアルタイムなアニメーション補間」に向いてると思うのですが
jbpb0

2021/05/30 16:04

hayataka2049さん 質問の「更に追記」のグラフは傾きの条件を無視してるように見えるので、その条件は無視しました その条件が必須なら、目的に合いませんね
guest

0

関数というと計算するものだと考えていらっしゃるかもしれませんが、データを表で持っておいて、その表を引いて補間すれば、実用的には十分です。

c51d4802ecc4f6fa31d0a2da7ccf16db.pngのグラフのデータをもう少し細かい間隔で作っておき、xとyを入れ替えたものを多少いじれば右下の関数の表も作成できます。

一個の関数を表す表は64要素ぐらい作っておけば十分ではないですか。それだとdoubleで持っても512バイトですね。変化量を128通り持っても64キロバイトです。

そうやって作った表をC/C++の配列データとして書き出してリンクしておけば、実行時には表引き+補間で計算できるので、計算コストは最小限で済みます。

投稿2021/05/26 04:08

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/05/26 04:11

テーブルで持てばよくね? という話は既存回答のコメント欄にて既出です.
XVK

2021/05/26 04:18

ありがとうございます テーブル参照の補間は最終手段として考えております (シェーダーへの応用が難しくなるため) また画像のグラフを細かくすることに苦戦しています x=0付近はいくらでも細かくできるのですが、大きい方向への分割計算が上手く行ってない状況です (もしくは根本的に幾何学的に破綻しているのかもしれないですが)
ppaul

2021/05/26 04:33

すみません、コメント欄は読んでいませんでした。 > fanaさん 今の計算式をで関数を決めなければならないというのも絶対ではありませんね。 極端に言えば、手で引いた曲線を元に高次関数で補完して表を作っても良いのです。 >XVKさん
ppaul

2021/05/26 04:39

シェーダーへの応用が難しくなるため というのは良くわかりませんが、表を引いて補完する関数をC/C++で作成してライブラリ化しておけば、たいていの言語から言語間結合で呼び出すことは可能です。
fana

2021/05/26 05:03

> すみません、コメント欄は読んでいませんでした。 > fanaさん 既にそこに質問者からのコメントも付いているので見てね的な情報提供の意図ですよ.(念のため明言) 元々は「陽関数である必要が…」という流れで書かれた話ですが, それ自体が一つの話題として切り分けられたことは良いことだと思います. (「回答」の形とする以外にそのように話を分ける手段が無いですし.)
XVK

2021/05/26 05:06

これは今はさほど重要視していないのですが HLSLなどで記述するシェーダーでのプログラマブルなパイプライン処理にて、表を引く工程でif文の使用が恐らく避けられず速度的な問題が発生しそうです (勉強中のため実は表引きの機能も用意されているのかもしれないですが) 参考に貼らせていただいたサイトのシグモイド関数はそこら変も考慮されているようでしたので同等のことができればいいなという状態です
fana

2021/05/26 05:12 編集

表引き + 内挿 の処理に条件分岐が必要でしょうか? (暗黙的に「入力値 x が有効な値域内である」のだとすれば,分岐は要らないような?)
XVK

2021/05/26 05:23

HLSLにはclampやlerp(線形補間)といった機能が提供されているようですね 杞憂なのかもしれません
guest

0

端点の傾きは妥協して,
放物線 f(x) = a*x*x + b*x で,
左下の点(0,0) と,制御点 (x,1-x) までを結ぶ曲線を表すことを考えました.

制御点の座標が与えられた際に,

  • 制御点を通る
  • 制御点での傾き = 1

という条件の下で,aとbの値を解いてやります.
下図は制御点の x座標 = 0.3 での例です.
青いのが求めた放物線で,赤いのがそれを直線 y=1-x を境に反転したものです.

この2本の曲線を,x=0.3を境に繋いでやれば,一本の曲線になります.
こんなのじゃダメですかね?

イメージ説明

投稿2021/05/26 03:38

編集2021/05/26 03:39
fana

総合スコア11708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

XVK

2021/05/26 03:50

ありがとうございます なるほどそういうやりかたもあるんですね 助かります
fana

2021/05/26 04:46

f(x)を反転した赤い側も2次関数だから,必要ならば解の公式を持ち出してきて y= g(x) の形で書ける. ↓ とりあえず2つ陽関数 f(x),g(x) で曲線を表すことはできている. まぁ,使う際にはxの座標によってf(x)とg(x)のどちらを用いるべきか? の判定が必要にはなるだろうけども.
guest

0

質問の「更に追記」のグラフでは、傾きの条件を無視してるように見えます
それでいいなら、これはいかがでしょうか?
B-スプライン曲線

投稿2021/05/26 00:45

jbpb0

総合スコア7653

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2021/05/26 00:50

用途からすると陽関数である必要がある気がします
jbpb0

2021/05/26 01:05

ozwkさん たしかに、所望のxになるtをまず探索して、それからyを計算、みたいになりますね どれくらい誤差を許容するのかによるけど、リアルタイムなアニメーション補間だと、計算コスト的に厳しいですかね
XVK

2021/05/26 01:23

お恥ずかしながら陽関数という言葉を今知りました。 まさにそれです。 質問文に追記したいと思います。
fana

2021/05/26 02:34

ベジェなら > 傾きの条件 を満たせるのかな. (掴んで動かせる点の位置から,適当にベジェで使う2点をでっち上げる感じで)
guest

0

スプライン曲線でよいのではないでしょうか.


この条件だと,こんな感じの弓っぽい形になっちゃうかも?

イメージ説明


妥協点

妥協して良い所なのかどうかは不明ですが,
両端の傾きを妥協するならば,適当な(三角関数とか放物線とか,何かしらの)山なりな形の曲線を45度回転すれば良い気がしますが…

投稿2021/05/25 09:21

編集2021/05/25 10:45
fana

総合スコア11708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

XVK

2021/05/25 09:38

スプラインにおいて始点と終点で傾きの設定は可能でしょうか? 検索してみたところ『任意の点を必ず通る補間』という(3次スプライン?)感じがしたため傾きの扱い方がよくわかりません (もしくは始点と終点の超近傍に接線を無理やり設定するような点を配置するイメージでしょうか?) もしご存知でしたら有用なサイトへ案内お願いできませんか?
fana

2021/05/25 10:03

スプラインの係数を求める際に, 端点での傾きを用いますので,少なくともそこだけは守られます. つまり,質問で指定されている3つの点を通り,且つ,2つの端点で所望の傾きを持つ曲線になります. ……なのですが,(自分で回答しといて何ですが) その曲線が「良い形」になるかどうかは不明です. 条件だけは満たすけどね…みたいな形になるかもしれません.
fana

2021/05/25 10:35 編集

例えば,通過点が(0.5, 0.5) である場合とかを考えてみれば, 端点での傾きを守りつつ3つの点を通るためには,どうやったってぐにゃぐにゃした曲線にならざるを得ない. ( (0,0)から一旦真上に飛び出して,でも(0.5, 0.5)を通って,最後は(1,1)に真左から飛び込まないとならないから ) でも果たしてそれが所望の形状なのか? っていう. (→通過点をてきとーな位置に置いた場合に,こんな形になっちゃうかも? という雰囲気の絵を回答内に追加した)
fana

2021/05/25 10:22 編集

有用なサイト…は知らないけども,例えば2つの点の間を f( t ) = a*(t^3) + b*(t^2) + c*t + d みたいな3次関数(ここで,t=0~1で,{a,b,c,d, f(t)}は2次元のベクトル)で表してやろうとか考えると, f( 0 ) = 一方の端点の座標 f( 1 ) = 他方の端点の座標 f'( 0 ) = 一方の端点での接線方向ベクトル f'( 1 ) = 他方の端点での接線方向ベクトル という条件を与えてやることで,{a,b,c,d} を解くことができる. これは2点の場合だけど スプラインの場合,こんな感じの計算を「各"隣り合う2点を結ぶ線"が互いに滑らかにつながるなるように」みたいな感じで全体を決める……んだったハズ. (点列の各点の位置での傾きをどうやって与えてやるか? というあたりの違いで方法が異なる,みたいな)
XVK

2021/05/25 11:15

>山なりな形の曲線を45度回転すれば良い気がしますが… 数学にあまり明るくないため理解が及んでいないのですが 2次元空間でベクトルを回す方法はわかるのですが関数f(x)を回すというのは可能なのでしょうか? >この条件だと,こんな感じの弓っぽい形になっちゃうかも? f(0)の解が二個あるのはどういうことでしょう?
fana

2021/05/26 01:02

> 関数f(x)を回す 曲線上の各座標を回転すればよいですね. (加えて,ちょっとした位置とスケールの調整も必要でしょうけども) > f(0)の解が二個あるのはどういうことでしょう? この例は,条件{3点を通る,端点での向き}だけを満たすように2次元平面上に曲線を描いたので, 「xに対してyが1つ」みたいな制限が無いものになっています.
XVK

2021/05/26 01:36

ozwkさんからご指摘を頂いたように私が欲しているのは陽関数という名前がついていることがわかりました 関数の回転については結構しんどそうです x=xcosθ-ysinθ y=xsinθ+ycosθ を代入するところまでは容易であることはわかるのですが、おそらくこのあとまたY=の陽関数の式に戻すとき結構な数学的素養を要求されるか、ものによっては不可能な感じがします
fana

2021/05/26 02:44

> 陽関数 とのことですが,そこにこだわる必要がどれだけあるのでしょうか? 例えば,曲線の関数自体は陽関数ではないとしても, 前処理として「xに対するy値のテーブル」みたいなのを(いくらでも時間をかけて)計算しておいて, 実際に使う際にはそのテーブルから値を引くようなことにはできないのでしょうか? (ある程度の細かさのxでテーブルを用意しておき,その間のxについては線形補間とかで済ますとか)
XVK

2021/05/26 02:57

事前計算のテーブルから引いて任意のXから一つのYを出すのも妥協点だとは思っています ただ個人的には結構シンプルな要求だとおもっていて(たとえば円の方程式に類するような)先人が名前のついた関数を実はすでに発明されているのではという期待を持っています 参考で貼らせていただいたサイトではS字カーブのシグモイド関数というものを解説されていますが、そのような名前付きのものがあればなあという感じです またテーブル参照でif文を経由するより数式でバチっと出すことがもし可能であればシェーダーへ応用するさいの計算において有利になると思われるためテーブル参照は最終手段として考えております
fana

2021/05/26 05:15

(※MEMO : テーブルを使う話については,そのものずばりな内容の回答が付いたので,そちらへ)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問