HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2021
0726
大富豪斜めの移動について(座標指定)13解決


大富豪

リンク

2021/7/26(Mon) 18:56:44|NO.93319

お世話になります。
【斜めに移動させる座標指定について】お伺いします。

例えば、

x1=50:y1=41
x2=198:y2=346
(随時変化します)

※line x1,y1,x2,y2(右下がりの斜めの線)

repeat
line x1,y1,x2,y2
pos x1,y1:mes "●"
※x1,y1の増分指定
loop

line上に●を動かしたいです。
※(x1,y1)から(x2,y2)に移動

tanやatanを使いましたが割り算でゼロが発生したりうまく行きません・・・。

およその角度を割り出し移動量を大きくして、
例えば x+=10:y+=16とかすれば近似の角度での移動にはなりますが、
増分を大きくしたくありません。


そもそも、
x+=1pixel: y+=x*0.62pixel とかが必要になると思うので無理でしょうか…。
(可能ならx+=1を基準にしたい)

※360度対応可能な(x1,y1)の増分指定は何かよい方法はありますか?
(元の数値はx1,y2,x2,y2のみ)



この記事に返信する


wl

リンク

2021/7/26(Mon) 19:55:17|NO.93320

既にご存知かもしれませんが、
a=0 と代入すると、aは整数(int)型の変数になります。
a=0.1 と代入すると、aは浮動小数点数(double)型の変数になります。(小数が扱える)
整数型の変数に小数を加算しても、計算の際に小数点以下は切り捨てられます。
よって小数を扱いたい場合は、最初に変数を小数値で初期化する。
(ddim a とかでもいい。)

また、計算に小数と整数を混同させる場合、
最初に計算される演算子の左側の変数の型に全て合わせられるので、注意してください。

a=10.5
b=7
mes a+b //17.5
mes b+a //17

もしかすると、atanなどを使った計算の際に0除算エラーが出たのもこの辺が原因じゃないでしょうか。



大富豪

リンク

2021/7/26(Mon) 20:41:12|NO.93322

>wl様
ご返答ありがとうございます。

はい、計算式の場合は、5.00*1.82=とか、
桁数を合わせておく必要があったりするのは分かるのですが、
座標指定となると・・・??という感じです。

座標の差分(x2-x1,y2-y1)を元に、
アークtanの数値を出すのは分かるのですが...。
(tan45で1.000(x,yの増分が等しい)、tan60なら1.7321など)

xy移動座標を使った増分指定が、
x+=1 の時は、y+=x*1.7321(tan60)になりますが、
tan0度の時は tan数値が=0.0000になるので、
たぶん0除算エラーになるんだと思います。

そもそもPCの物理的に、
例えば「x+=1pixel の時 y+=x*1.732..pixel」
という細かい「座標指定」が不可能なのかと・・・。

tan0(=360)=0.000 〜 tan45=1.000 ...
...〜 tan89.9=572.9572 〜 tan359度
の近似でx,y増分を計算するしかないんですかね^^;



とあるプログラマ

リンク

2021/7/27(Tue) 01:45:46|NO.93325

「2座標間を一定の増分(速度)で点を移動させたい」という認識で合っているでしょうか?
であれば以下の内容でどうでしょうか。


そちらがどういう計算式を書いてらっしゃるのか分かりませんが、少なくともこの書き方であれば0除算エラーは発生しないと思います。

また精度に関してですが、"相対な変数操作・比較" ではなく "絶対な変数操作・比較" であればある程度誤差は軽減できます。
変わらない数値であれば毎度計算するのではなく、あらかじめ変数に保存しておくという方法ですね。(以下のコードで言うと、最初のrepeat 〜 2つ目のrepeat の部分)


しかしながら、doubleでも表現可能な数値には限りがありますから、どうしても微細な誤差は生まれてしまいます。

さらに、大富豪さんが仰る通り座標は整数型なので、完璧に綺麗な移動というのは難しいです。
どうしてもやりたい場合は、描画対象物のアンチエイリアシング処理でどうにか見かけ上は綺麗な移動にできるかもしれませんが、処理量的にも現実的とは言い難いですね…


#define global ctype round(%1) double(strf("%%0.0f", %1)) ; 四捨五入 #module // =================== // 2点座標から角度を算出 // =================== #defcfunc CalcAngle int x1, int y1, int x2, int y2 // コンピュータ座標の原点は左上であることに注意 return atan(y2 - y1, x2 - x1) // =================== // 2点座標間の距離を算出 // =================== #defcfunc CalcDist int x1, int y1, int x2, int y2 return absf(sqrt(powf(x2 - x1, 2) + powf(y2 - y1, 2))) // =================== // 角度と距離から座標を取得 // =================== #deffunc CalcCoord array out, double angle, double dist ddim out, 2 out(0) = dist * cos(angle) out(1) = dist * sin(angle) return // =================== // 指定座標を中心に円を描く (半径)) // =================== #deffunc DrawCircle int x, int y, int radius if (radius <= 0) : return circle x - radius, y - radius, x + radius, y + radius, 1 return #global // =============================================================== // モジュール終わり 以下プログラム本編 // =============================================================== randomize screen 0, 1280, 720 repeat // 座標2つ (XとYで別の変数にすると面倒なので配列にする) dim pointA, 2 dim pointB, 2 pointA = rnd(ginfo_winx), rnd(ginfo_winy) pointB = rnd(ginfo_winx), rnd(ginfo_winy) // 増分 ddim increment, 1 increment = 1.0 + (double(rnd(2001)) / 1000.0) // 1.0 〜 3.0 で増分をランダムに設定 // 2点間角度 ddim angle, 1 angle = CalcAngle(pointA(0), pointA(1), pointB(0), pointB(1)) // distPoint:現在の移動量 // distTotal:全体の移動量 ddim distPoint, 1 ddim distTotal, 1 distTotal = CalcDist(pointA(0), pointA(1), pointB(0), pointB(1)) // 点Bに到達するまでループ repeat redraw 0 color 255, 255, 255 boxf // 線分 color 0, 0, 0 line pointA(0), pointA(1), pointB(0), pointB(1) // 2点間角度と現在の距離から座標を取得 ddim buf, 2 CalcCoord buf, angle, distPoint // 点A基準なので考慮、四捨五入でより精度の向上 buf(0) = round(buf(0) + pointA(0)) buf(1) = round(buf(1) + pointA(1)) // 円描画 color 255, 0, 0 DrawCircle buf(0), buf(1), 2 // おまけで情報も書いちゃう pos 0, 0 color 0, 0, 0 mes "increment: " + increment mes "pointA: (" + pointA(0) + ", " + pointA(1) + ")" mes "pointB: (" + pointB(0) + ", " + pointB(1) + ")" mes "point of circle: (" + int(buf(0)) + ", " + int(buf(1)) + ")" mes "distPoint: " + distPoint mes "distTotal: " + distTotal redraw 1 // 現在の移動量が全体に到達すれば完了 if (distPoint >= distTotal) : break // 増分を増やして距離を進める (おまけ範囲チェック付き) distPoint = limitf(distPoint + increment, 0.0, distTotal) await 16 loop // 2秒後次のループへ wait 200 loop stop



大富豪

リンク

2021/7/27(Tue) 03:06:10|NO.93326

>とあるプログラマ様
ご返答頂き誠にありがとうございます。

-----
おお!
>「2座標間を一定の.....
はい、まさに思い描いていた動きです。
完璧にしか見えません^^;

まだ理解していない用語が多いので応用が難しいですが、
なんとか勉強しながら解読させて頂きます。

-----
もし良ければ1つ教えて頂けないでしょうか。

increment = 1.0 + (double(rnd(2001)) / 1000.0)
 2001の数字の意味と、
 増分範囲が 3.0 までになっている理由、
 *.0~*.0 と小数になっている理由について。

-----
line表示は角度によっては少しカクカクしますが、
ガイドラインのような用途もありますので非表示に出来ます。
”アンチエイリアシング処理”も参考にさせて頂きます。

-----
当初は二点のx/y座標差分からtanΘの数値を出して、
x/yの移動量に変換できると思っていたのですが、
よく考えたら x+1(px)に対して y+=x*0.7813(px)という
PCでは不可能と思われる数字になり・・・
(x/y座標の増分指定だけで斜め移動させる)



usagi

リンク

2021/7/27(Tue) 09:41:08|NO.93329

増分を足すというより、xに対するyの位置を求めるという考え方もありますね。

#module ; 線形補間 #defcfunc lerp int ax1, int ay1, int ax2, int ay2, int ax return ay1 + (ay2 - ay1) * (ax - ax1) / (ax2 - ax1); #global x1=50:y1=41 x2=198:y2=346 x = x1 : y = lerp(x1,y1,x2,y2,x) pos x,y repeat abs(x1-x2) x+=1 ;整数で1づつ足したい y = lerp(x1,y1,x2,y2,x);xからyの位置を求める line x, y await 16 ;確認用 loop

あとは標準命令であればサンプルのease_test3.hspあたりを参考にするとかどうでしょうか。



大富豪

リンク

2021/7/27(Tue) 10:18:25|NO.93330

>usagi様
ご返答頂きありがとうございます。
おお?動いていますね!…動いてます。

そもそも、#*****の、
moduleやdefcfuncなどの仕組みを全く理解していないので
私の能力では応用(ソース本編に盛込み)が難しいのですが、

ay1 + (ay2 - ay1) * (ax - ax1) / (ax2 - ax1)
y = lerp(x1,y1,x2,y2,x)

このあたりの計算式が数学の公式のような解釈になるんですかね。

lerp、式の意味と、(x1,y1,x2,y2,x)にxが入っている理由を
宜しければ教えて頂けないでしょうか。



usagi

リンク

2021/7/27(Tue) 10:37:47|NO.93332

仰るっとりその式がキモで、
”線形補完”でネット検索するといろいろ詳しい説明があるかもです。

lerpはLinear interpolationの略で、defcfuncは好きな命令を作れるので、適当に名前を付けました。
senkeihokanでも大丈夫です。

スクリプトエディタでF1を押してマニュアルみるとわかりやすいと思います。



大富豪

リンク

2021/7/27(Tue) 10:43:37|NO.93333

>usagi様
再びすみません。

x1=50:y1=41
x2=198:y2=346
ですが、

(360度対応を想定していますが、)
x1=x2の時に0除算エラー、
x1>x2の時(第二と第三象限方向)に動作しません。
多分ですけど;



大富豪

リンク

2021/7/27(Tue) 10:52:22|NO.93334

>usagi様
ありがとうございます。
(投稿のタイミングがアレでした…)

”線形補完”勉強させて頂きます。
他の件も承知しました、
参考にさせて頂きます。

x1=50:y1=41
x2=198:y2=346
本文では任意の数値で書きましたが、
この二点間の座標を結ぶ線分?は360度で想定しています。

y1=y2の水平移動はいいのですが、
x1=x2(垂直移動)だとゼロエラーが出ます。
左上方向と左下方向の座標だと逆に動きますが、
符号のアレが要るんでしょうか。



とあるプログラマ

リンク

2021/7/27(Tue) 11:16:54|NO.93335

なるほど、線形補間を使う方法がありましたね。

そちらのほうが簡単ですが、直接の指定距離の移動ではないことだけ注意ですかね。
どちらを使うかは用途や目的次第でしょうか。


〜〜〜〜〜〜〜


>> もし良ければ1つ教えて頂けないでしょうか。
>>
>> increment = 1.0 + (double(rnd(2001)) / 1000.0)
>>  2001の数字の意味と、
>>  増分範囲が 3.0 までになっている理由、
>>  *.0~*.0 と小数になっている理由について。

rnd(N)は、0以上N"未満"の乱数を生成する関数です。
未満なので、最大2000を生成したい場合は+1して2001です。

0〜2000を1000で割っているということは、0.0〜2.0の範囲で最小差が0.001の実数値を出しています。
例えば乱数で1732という数字が生成されれば、1732 / 1000 つまり 1.732 という実数値が出されます。

これに 1.0 + しているので 1.0〜3.0 までの実数値が出されるわけです。

今回は増分も実数で作ってみたので、小数になっています。



〜〜おまけ〜〜

HSP3は計算式を入力した場合、一番最初(左) の型に合わせられます。

なので

hoge = 1.0 + 2

と書くと、hogeは実数型で 3.0 になりますが、

hoge = 2 + 1.0

と書くと、hogeは整数型で 3 になってしまいます。


もし先に違う型を書きたい場合は、

hoge = int(1.0) + 2
hoge = double(2) + 1.0

のようにキャスト(型変換)してください。


また、キャストしていなければその型のまま演算されるので、例えば

hoge = 1.0 + double(2 * 3.5)

とした場合は、

1.0 + double( 整数 2 * 整数化されてしまった 3) = 1.0 + double(6)

となりhogeは7.0になってしまいます。

数値の部分が変数でも同様に、変数の型に依存します。


整数型小数型混合のスクリプトを書いていると、慣れている人でも割とやりがちなミスなので注意してください。



usagi

リンク

2021/7/27(Tue) 11:19:01|NO.93336

x1=x2 だとXを1づつ足していきたいというご要望が満たせませんので、
素直に分岐するのが良いと思います。

if (x1-x2) == 0 { line x1,y1,x2,y2 } else { // こっちで補完処理 }
また、x1>x2を想定される場合はスワップしてください。
仰る通り符号のアレになるので(1づつ足せないので)
そうすれば逆方向にも対応できます。

if x1>x2 { x1^=x2: x2^=x1: x1^=x2 y1^=y2: y2^=y1: y1^=y2 }

たぶん、数学お詳しそうなのでもうお分かりですよね。

#module ; 線形補間 #defcfunc lerp int ax1, int ay1, int ax2, int ay2, int ax return ay1 + (ay2 - ay1) * (ax - ax1) / (ax2 - ax1); #global *MAIN x1=mousex: y1=mousey x2=ginfo_winx/2: y2=ginfo_winy/2 if x1>x2 { // 1づつ足せないのでスワップ(マイナスになるから) x1^=x2: x2^=x1: x1^=x2 y1^=y2: y2^=y1: y1^=y2 } if (x1-x2) == 0 { // 差が0なので1づつ足せない line x1,y1,x2,y2 } else { // 1づつ足せる場合 x = x1 : y = lerp(x1,y1,x2,y2,x) pos x,y repeat abs(x1-x2) x+=1 ;整数で1づつ足したい y = lerp(x1,y1,x2,y2,x);xからyの位置を求める line x, y loop } await 16 goto *MAIN



大富豪

リンク

2021/7/27(Tue) 12:29:05|NO.93337

>とあるプログラマ 様
>usagi 様

お二方とも大変ありがとうございます。


ご解説やソースアレンジのお手数も頂きありがとうございます、
用語や仕組みの理解に時間が掛かると思いますが、
やはり”勉強しながら解読”させて頂きます。

ほぼ解決に向かっていると思いますが、
私にとっては超ハイレベルなソースに少々目が回ってきましたので…@@;

また不明点あればコメントするかもしれませんが、
暫く様子見という事で宜しくお願いします。


御両名に改めてお礼を申し上げます、
誠にありがとうございます。



大富豪

リンク

2021/7/30(Fri) 14:42:48|NO.93377

>とあるプログラマ 様
>usagi 様

ご対応ありがとうございました。

まだ理解してない部分もありますが、
ほぼ解決に近づいたかと思いますので、
解決済みフラグに致しました。

ありがとうございました。



ONION software Copyright 1997-2023(c) All rights reserved.