三角函数与万花尺(初稿)

    本人语文能力不佳,叙述可能不是很清晰。如果谁能看到这篇文章,还请提出一些建议,毕竟这只是初稿。谢谢。

    书上说过,一个角θ的终边与单位圆的交点坐标为(cosθ,sinθ),我们很容易推导出角θ的终边与圆心位于原点、半径等于r的圆的交点坐标为(rcosθ,rsinθ)。当θ由0逐渐增大到2π时,我们就可以计算出圆上各点的坐标。当然,我们需要决定究竟计算几个点,这就是变量n的作用。由于大圆的位置是固定的,而小圆位置是不断变化的,所以大圆上各点的坐标可以直接加上大圆圆心的坐标来让大圆显示在窗口中一个确定的位置,而输出的小圆上各点坐标仍然是其相对于圆心的坐标。

    接下来,我们进行小圆圆心坐标和笔尖坐标的计算。首先,我们以大圆圆心为原点建立平面直角坐标系,并假定初始状态时小圆与大圆的切点在x轴正半轴上,设小圆绕大圆圆心逆时针转过的角度为α,即以x轴正半轴为α的始边,以原点向小圆圆心作的射线为α的终边。我们很容易判断两圆的切点也在α的终边上,那么两圆圆心间的距离就是两圆半径之差,而小圆圆心就在以这个距离为半径的圆周上运动。用ra表示大圆半径,rb表示小圆半径,则小圆的圆心坐标bx=ax+(ra-rb)*cosα,by=ay+(ra-rb)*sinα。之所以要加上大圆的圆心坐标,是为了得到小圆圆心在窗口中的绝对坐标,再加上前面得到的相对坐标,就可以显示小圆的运动了。可是,我们要注意的是,屏幕坐标的y轴的正方向是向下的,因此实际显示出的图形是上下翻转的,所以by应为ay-(ra-rb)*sinα。
    从万花尺的原理中,我们可以知道,两圆之间发生的是相对滚动而不是滑动,因此接触点在两圆圆周上移动的路程相同,意即两圆切点在两圆上移动的弧长相等。由于切点在α的终边上,所以α可以表示切点在大圆圆周上转过的圆心角,那么我们设切点在小圆圆周上转过的圆心角为β,就可以得到l=∣α∣*ra=∣β∣*rb。然而小圆相对于大圆圆心逆时针转动时其相对于自身的圆心是顺时针转动,因而α为正、β为负,所以α*ra=-β*rb,β=-(ra/rb)*α。不过,β是小圆相对于切点顺时针转过的角度,而切点本身还相对于小圆圆心逆时针转过了α角——我们过小圆圆心作一条平行于x轴的直线就会发现这个角与α是同位角——所以小圆相对于自身圆心顺时针转过的角度为α+β=(1-ra/rb)*α。显然,笔尖划过的角度也是(1-ra/rb)*α,因为笔尖在小圆上的位置是固定的,会随着小圆一起绕圆心转动。用d表示笔尖到小圆圆心的距离,并且设θ=(1-ra/rb)*α,那么与上文同理得笔尖的窗口坐标为x=bx+d*cosθ,y=by-d*sinθ。
#include<stdio.h>
#include<math.h>

int main(void){
    double d=50,ra=150,rb=50;
    int i,m=720,n=360;
    int ax,ay,bx,by,ax0=384,ay0=288;
    double alpha=0,theta=0;

    FILE *fp=NULL;
    fp=fopen("wanhuachi.txt","w");
 
    if(fp==NULL){
        return -1;
    }

    for(i=0;i<n;i++){

        ax=ra*cos(theta)+ax0;
        ay=-ra*sin(theta)+ay0;
        bx=rb*cos(theta);
        by=-rb*sin(theta);

        fprintf(fp,"%d,%d,%d,%d,",ax,ay,bx,by);
//      fprintf(fp,"ax=%d,ay=%d,bx=%d,by=%d,theta=%f\n",ax,ay,bx,by,theta);
        theta+=2*3.1415926/n;
    }

    for(i=0;i<m;i++){

        bx=ax0+(ra-rb)*cos(alpha);
        by=ay0-(ra-rb)*sin(alpha);

        theta=(1-ra/rb)*alpha;
        ax=bx+d*cos(theta);
        ay=by-d*sin(theta);

        fprintf(fp,"%d,%d,%d,%d,",bx,by,ax,ay);
//      fprintf(fp,"bx=%d,by=%d,x=%d,y=%d,alpha=%f,theta=%f\n",bx,by,ax,ay,alpha,theta);
        alpha+=2*3.1415926/n;
    }

    fclose(fp);
    fp=NULL;

    return 0;
}

这样,我们就得到了各点的坐标,然后我使用haribote操作系统来完成图形显示(见《30天自制操作系统》)。

#include "apilib.h"
int p[360*12]={};

void HariMain(void){

    int m=720,n=360;
    int win,buf,timer,i=0,j=4*n;
    api_initmalloc();
    buf = api_malloc(768 * 576);
    win = api_openwin(buf, 768, 576, -1, "wanhuachi");
    api_boxfilwin(win+1, 5, 24, 762, 570, 0);
    timer = api_alloctimer();
    api_inittimer(timer, 128);

    for (;;) {

        api_boxfilwin(win+1, 5, 24, 762, 570, 0);

        for(i=0;i<4*n;i+=4){
            api_point(win+1,p[i],p[i+1],3);
            api_point(win+1,p[j]+p[i+2],p[j+1]+p[i+3],4);
        }

        api_linewin(win+1,p[j],p[j+1],p[j+2],p[j+3],14);

        for(i=4*n;i<=j;i=i+4){
            api_point(win+1,p[i+2],p[i+3],3);
        }

        api_refreshwin(win, 5, 24, 762, 570);

        api_settimer(timer, 1);

        j+=4;
        if(j>=4*n+4*m){
            j=4*n;
        }

        if (api_getkey(1) != 128) {
            api_end();
        }
    }
}

程序很简单,运行效果与设想一样,证明我的思路是正确的。成功!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值