平面波動(dòng)方程實(shí)現(xiàn)線(xiàn)條波動(dòng)效果

前兩天看到一個(gè)水波的動(dòng)畫(huà)圖,形如下面這樣子,突然波動(dòng)方程在記憶深處就復(fù)活了。發(fā)現(xiàn)這個(gè)方程可以很好玩,雖然只是二維的,但還是分享給大家。


waveWater.gif

* 波動(dòng)方程的推導(dǎo)

已知波源A的振動(dòng)方程:y=Acos(ωt+φA?)。假設(shè)波沿x軸正方向傳播速率記為u,沿x軸負(fù)方向傳播速率記為V。以O(shè)點(diǎn)為坐標(biāo)原點(diǎn),A點(diǎn)的坐標(biāo)為Xa,以水平向右為x軸的正方向建立坐標(biāo)系。
當(dāng)波沿x軸正方向傳播的時(shí)候,如下圖所示,在x軸正方向上任取一點(diǎn)P,與o點(diǎn)相距Xp。當(dāng)?shù)貌ㄏ蛴覀鞑r(shí),P點(diǎn)的振動(dòng)落后于A點(diǎn),落后的相位為ω (Xp-Xa)/u。可得P點(diǎn)在任意時(shí)刻t的位移:y=Acos[ωt-ω (Xp-Xa)/u+φa]= Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]。可參考:如何求解平面簡(jiǎn)諧波的波動(dòng)方程。

沿x軸方向傳播的平面簡(jiǎn)諧波

得到的這個(gè)方程叫平面簡(jiǎn)諧波動(dòng)方程,形如:

y=Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]

它能描述任意時(shí)刻任意點(diǎn)在Y軸方向上的位移y。加上一個(gè)微分的思想,取一個(gè)X+dx值,然后計(jì)算出對(duì)應(yīng)的y值,將這些n個(gè)y值連成一條線(xiàn)就是任意時(shí)刻的波形圖。當(dāng)t在流逝的時(shí)候這個(gè)波形圖不斷的在重畫(huà),就形成了一個(gè)波動(dòng)圖了!在代碼前先分析一下這個(gè)方程:y=Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]

* 方程的物理意義

  • y是y坐標(biāo)的位置,也就是我們要計(jì)算點(diǎn)的y值
  • A是振幅,就這個(gè)波振動(dòng)幅度的大小
  • ω是頻率,可以理解為是振動(dòng)的快慢,ω = 2 / T(T是周期)
  • t就是時(shí)間了,時(shí)間是均勻(絕對(duì)時(shí)空觀)往前走的
  • u描述的是波的傳播速率,波速=波長(zhǎng)/周期
  • φa描述的波源的初相位,在圖上看就是這個(gè)點(diǎn)是在x軸(φa=0)上開(kāi)始振動(dòng),還是在x軸上多少或下多少(cosφa)的地方開(kāi)始振動(dòng)。

只是為了實(shí)現(xiàn)這種效果,我們就利用最簡(jiǎn)單的簡(jiǎn)諧波了。此時(shí):

  • 波的初相為φa = 0
  • 波源就在Xa處,即Xa = 0

此時(shí)波動(dòng)方程就變成了:

* 最簡(jiǎn)方程

y=Acos(ωt- ω Xp/u)

是不是簡(jiǎn)單了好多!這樣再設(shè)兩個(gè)變量,一個(gè)是周期T和波長(zhǎng)K,根據(jù) “波速=波長(zhǎng)/周期(u = K / T)” 和 ω = 2π / T。波動(dòng)方程就可以推導(dǎo)成這個(gè):

y = Acos2π(t/T - Xp/K)

這樣代碼的樣子就出來(lái)了,忍不住開(kāi)始代碼了吧!

*代碼實(shí)現(xiàn)

利用Quartz2D就可以簡(jiǎn)單實(shí)現(xiàn),用一個(gè)UIView來(lái)實(shí)現(xiàn),命名為WaveView(所有代碼都在WaveView.m文件里面)。
設(shè)置波的基本屬性:

    @interface WaveView ()
{
    CGFloat screenWidth;
    
    float A;    // 振幅
    float t;    // 時(shí)間變量
    
    float T;    // 周期
    float K;    // 波長(zhǎng)
}
@end

在init方法里面初始化波的基本屬性值:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        // 設(shè)置波的基本屬性()
        A = 6;
        t = 0;
        T = 1.0;
        K = 80;
        
        [self setBackgroundColor:[UIColor clearColor]];
        
        // 設(shè)置刷新圖像的頻率,0.3秒人眼就很難分辨出來(lái)了
        [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(animateWave) userInfo:nil repeats:YES];
    }
    return self;
}

啟動(dòng)動(dòng)畫(huà):

-(void)animateWave
{
    // 這里是時(shí)間機(jī)器,如果和刷新圖像的時(shí)間間隔一樣,那么就是正常時(shí)間的速度
    // 如果大于刷新時(shí)間間隔,那么時(shí)間就走的很快,是平常的多少倍自己去計(jì)算
    // 如果小于刷新時(shí)間間隔,那么時(shí)間就走的慢
    
    t+=0.05; // 這里比實(shí)際時(shí)間快
    
    [self setNeedsDisplay];
    

動(dòng)畫(huà)算法的實(shí)現(xiàn):

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGMutablePathRef path = CGPathCreateMutable();
    CGContextSetLineWidth(context, 2);
    CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
    float y=axleXOnScreenHeight;
    CGPathMoveToPoint(path, NULL, 0, y);
    
    // 這是將x軸微分單位為1pt,就是每隔1pt計(jì)算一個(gè)y值,將所有的y值連起來(lái)就是一個(gè)波圖
    // 可以將微分單位設(shè)置大一點(diǎn),有不一樣的效果
    // y 值的計(jì)算就用推導(dǎo)出來(lái)的公式:y = Acos2π(t/T - Xp/K)
    for(float x=0;x <= screenWidth;x+=1){
        y =  A * cos(2*M_PI * (t / T -  x / K)) + axleXOnScreenHeight;
        CGPathAddLineToPoint(path, nil, x, y);
    }
    
    CGContextAddPath(context, path);
    CGContextDrawPath(context, kCGPathStroke);
    CGPathRelease(path);
}

效果如下,波長(zhǎng)為80,屏幕寬了375剛好5個(gè)波峰的樣子:


waveView.gif

其它好玩的效果(比如波動(dòng)過(guò)程中改變振幅的大小,就可以增加波的節(jié)奏感等):


waveViewZheXian.gif
waveViewWater.gif

waveViewUnkown.gif

是不是挺好玩的,可惜沒(méi)有接觸過(guò)3D引擎,不然整個(gè)世界都可以波動(dòng)起來(lái)。
趕緊試試!
源碼位置:https://github.com/stoull/Wave

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容