其他分享
首页 > 其他分享> > 创意画板延伸内容

创意画板延伸内容

作者:互联网

在完成了创意画板的基础功能实现后,我们就可以通过画板来绘制一些有趣的图形了

1.平面山脉图

 

效果图如上

山脉图是由一个个山峰构成的,所以先绘制一个山峰

而山峰的绘制算法是:先确定两个点A B,然后获取A B的中间点P,其中P的x值为A Bx轴向上的中点,P点的y值是以A By中值为基础,在(-k,k)范围内随机取值。算出P点后,对AP BP 进行如上的相通算法,不过k值要减小,迭代下去。在所有的点计算完成后,将点连线就获得了一个山脉曲线。

具体代码实现如下,其中N为迭代次数,K为初始缩小比例

//山峰
    public void shanfen(int x1,int y1,int x2,int y2,int N,Graphics gr,double k){
​
        int y = (y1 + y2)/2;
        if(N>1){
            int px,py;
            int ran = (int)((Math.random()*800-400)*k);
            px = (x1 +x2)/2;
            py = ran + y;
            k*=0.5;//等比缩小
            shanfen(x1,y1,px,py,N-1,gr,k);
            shanfen(px,py,x2,y2,N-1,gr,k);
​
​
        }else {
            gr.drawLine(x1,y1,x2,y2);
           
        }
​
    }

这样,你就获得了一张这样的山脉曲线

 

现在我们可以将颜色填充进去

注意到山脉曲线的绘制实际上是一条条线段构成的,所以我们可以在绘制每一条线段之后给线段下方填充颜色

这里要用到Polygon类中的填充方法

Polygon py = new Polygon();//创建Polygon类的对象
py.addPoint(x1,y1);//依次添加点,注意要依次添加,具体可以自己尝试
py.addPoint(x2,y2);
py.addPoint(x2,2000);//y值可以自己定
py.addPoint(x1,2000);
int r = (int)Math.random()*256;//这里我取随机颜色
int g = (int)Math.random()*256;
int b = (int)Math.random()*256;
gr.setColor(new Color(r,g,b,128));//128是指透明度
gr.fillPolygon(py);//填充方法
gr.setColor(new Color(0,0,0,255));//复原透明度

 

 

这样,我们就获得了一张这样的图形

然后山脉就是多绘制几次,我这里绘制了五次,就有了最初的效果了。

2.谢宾斯基地毯

效果图如下

 

 

谢宾斯基地毯的绘制原理是给定两个点A B,将以AB为对角点的矩形均分为九宫格,将九宫格中间部分涂黑,然后对剩下的八个宫格进行相同操作

具体代码如下,其中N为迭代次数

//谢宾斯基地毯
public void xiefu(int x1,int y1,int x2,int y2,int N,Graphics gr){
​
    if(N > 1){
        gr.fillRect(x1 + (x2 - x1)/3,y1 + (y2 - y1)/3,(x2 - x1)/3,(y2 - y1)/3);//画笔的填充方法
​
        xiefu(x1,y1,x1 + (x2 - x1)/3,y1 + (y2 - y1)/3,N - 1,gr);//分别对八个宫格进行迭代
​
        xiefu(x1 + (x2 - x1)/3,y1,x1 + 2*(x2 - x1)/3,y1 + (y2 - y1)/3,N - 1,gr);
​
        xiefu(x1 + 2*(x2 - x1)/3,y1,x2,y1 + (y2 - y1)/3,N - 1,gr);
​
        xiefu(x1,y1 + (y2 - y1)/3,x1 + (x2 - x1)/3,y1 + 2*(y2 - y1)/3,N - 1,gr);
​
        xiefu(x1 + 2*(x2 - x1)/3,y1 + (y2 - y1)/3,x2,y1 + 2*(y2 - y1)/3,N - 1,gr);
​
        xiefu(x1,y1 + 2*(y2 - y1)/3,x1 + (x2 - x1)/3,y2,N - 1,gr);
​
        xiefu(x1 + (x2 - x1)/3,y1 + 2*(y2 - y1)/3,x1 + 2*(x2 - x1)/3,y2,N - 1,gr);
​
        xiefu(x1 + 2*(x2 - x1)/3,y1 + 2*(y2 - y1)/3,x2,y2,N - 1,gr);
​
    }
​
}

其核心编程思想是跟山脉的绘制一样的,都是利用的迭代算法能存储数据的能力。

3.门格海绵

效果图如下

 

 

门格海绵是谢宾斯基地毯的立体版,想要绘制门格海绵,就需要先绘制出立体的正方体(矩形也行,这里我选择了正方体)

绘制立体正方体就是绘制我们看见的立体正方体的线,然后将不同的面填充不同的颜色以达到区分的效果

//绘制正方体
public void drawCube(int x1,int y1,int x2,int y2,Graphics gr){
​
    int dx = (x2-x1)/2;
    int dy = (y2-y1)/2;
​
​
    //绘线
    gr.drawLine(x1,y1,x2,y1);
    gr.drawLine(x2,y1,x2,y2);
    gr.drawLine(x2,y2,x1,y2);
    gr.drawLine(x1,y2,x1,y1);
    gr.drawLine(x1,y1,x1+dx,y1-dy);
    gr.drawLine(x2,y1,x2+dx,y1-dy);
    gr.drawLine(x2,y2,x2+dx,y2-dy);
    gr.drawLine(x1+dx,y1-dy,x2+dx,y1-dy);
    gr.drawLine(x2+dx,y1-dy,x2+dx,y2-dy);
​
    //填充颜色
    Polygon py = new Polygon();
    py.addPoint(x1,y1);
    py.addPoint(x2,y1);
    py.addPoint(x2,y2);
    py.addPoint(x1,y2);
    gr.setColor(new Color(200,100,100));//三个面填充不同的颜色,以便区分
    gr.fillPolygon(py);
​
    Polygon py1 = new Polygon();
    py1.addPoint(x1,y1);
    py1.addPoint(x2,y1);
    py1.addPoint(x2+dx,y1-dy);
    py1.addPoint(x1+dx,y1-dy);
    gr.setColor(new Color(100,200,100));
    gr.fillPolygon(py1);
​
    Polygon py2 = new Polygon();
    py2.addPoint(x2,y1);
    py2.addPoint(x2,y2);
    py2.addPoint(x2+dx,y2-dy);
    py2.addPoint(x2+dx,y1-dy);
    gr.setColor(new Color(100,100,200));
    gr.fillPolygon(py2);
​
}

这样,我们就可以获得一个看起来是立体的正方体

 

 

下一步就是进行迭代,与谢宾斯基地毯的思路一样,对正方体操作,然后再对需要重复操作的地方进行迭代,用N控制迭代次数

门格海绵是将正方体(长方体)均分成27份(以谢宾斯基地毯的划分形式),然后将每个面的中间正方体和中心正方体掏去。再对每个小正方体进行相同操作

效果是这样子的

 

具体代码如下: 

public void drawSponge(int x1,int y1,int x2,int y2,Graphics gr,int N){
    if(N > 1){
        int dx = (x2-x1)/2;
        int dy = (y2-y1)/2;
​
        //后面8个
        drawSponge(x1+2*dx/3,y1+2*(y2-y1)/3-2*dy/3,x1+(x2-x1)/3+2*dx/3,y2-2*dy/3,gr,N-1);
        drawSponge(x1+(x2-x1)/3+2*dx/3,y1+2*(y2-y1)/3-2*dy/3,x1+2*(x2-x1)/3+2*dx/3,y2-2*dy/3,gr,N-1);
        drawSponge(x1+2*dx/3+2*(x2-x1)/3,y1+2*(y2-y1)/3-2*dy/3,x2+2*dx/3,y2-2*dy/3,gr,N-1);
​
        drawSponge(x1+2*dx/3,y1+(y2-y1)/3-2*dy/3,x1+(x2-x1)/3+2*dx/3,y1+2*(y2-y1)/3-2*dy/3,gr,N-1);
        drawSponge(x1+2*(x2-x1)/3+2*dx/3,y1+(y2-y1)/3-2*dy/3,x2+2*dx/3,y1+2*(y2-y1)/3-2*dy/3,gr,N-1);
​
        drawSponge(x1+2*dx/3,y1-2*dy/3,x1+(x2-x1)/3+2*dx/3,y1+(y2-y1)/3-2*dy/3,gr,N-1);
        drawSponge(x1+(x2-x1)/3+2*dx/3,y1-2*dy/3,x1+2*(x2-x1)/3+2*dx/3,y1+(y2-y1)/3-2*dy/3,gr,N-1);
        drawSponge(x1+2*(x2-x1)/3+2*dx/3,y1-2*dy/3,x2+2*dx/3,y1+(y2-y1)/3-2*dy/3,gr,N-1);
​
        //中间四个
        drawSponge(x1+dx/3,y1+2*(y2-y1)/3-dy/3,x1+(x2-x1)/3+dx/3,y2-dy/3,gr,N-1);
        drawSponge(x1+dx/3+2*(x2-x1)/3,y1+2*(y2-y1)/3-dy/3,x2+dx/3,y2-dy/3,gr,N-1);
        drawSponge(x1+dx/3,y1-dy/3,x1+(x2-x1)/3+dx/3,y1+(y2-y1)/3-dy/3,gr,N-1);
        drawSponge(x1+dx/3+2*(x2-x1)/3,y1-dy/3,x2+dx/3,y1+(y2-y1)/3-dy/3,gr,N-1);
​
        //正面8个
        drawSponge(x1,y1+2*(y2-y1)/3,x1+(x2-x1)/3,y2,gr,N-1);
        drawSponge(x1+(x2-x1)/3,y1+2*(y2-y1)/3,x1+2*(x2-x1)/3,y2,gr,N-1);
        drawSponge(x1+2*(x2-x1)/3,y1+2*(y2-y1)/3,x2,y2,gr,N-1);
​
        drawSponge(x1,y1+(y2-y1)/3,x1+(x2-x1)/3,y1+2*(y2-y1)/3,gr,N-1);
        drawSponge(x1+2*(x2-x1)/3,y1+(y2-y1)/3,x2,y1+2*(y2-y1)/3,gr,N-1);
​
        drawSponge(x1,y1,x1+(x2-x1)/3,y1+(y2-y1)/3,gr,N-1);
        drawSponge(x1+(x2-x1)/3,y1,x1+2*(x2-x1)/3,y1+(y2-y1)/3,gr,N-1);
        drawSponge(x1+2*(x2-x1)/3,y1,x2,y1+(y2-y1)/3,gr,N-1);
​
​
    }else{
        drawCube(x1,y1,x2,y2,gr);
​
    }
}

注意,由于绘制次序的原因,后绘制的图形会覆盖先绘制的图形,所以绘制正方体的时候要先绘制大部分看不见的,就是后面的,在绘制前面的,先绘制下面的,再绘制上面的。

4.分形的绘制

IFS manual

 

 

 

以上的三个效果图是就根据我提供的网站中类似

 

这样的算法实现的

其中set1234的意思是,在你每次要算新的xy时随机等概率选取其中一组值进行计算,x0y0可以随机选取。

例如其中枫叶图形的绘制

//枫叶
public void drawMaple(double x,double y,Graphics gr){
​
​
    for (int i = 0; i < 7000000; i++) {
        int m = (int)(Math.random()*4);
        double xb = 0 ,yb = 0;
        if(m == 0){
            xb = 0.14*x + 0.01*y - 0.08;
            yb = 0.51*y - 1.31;
            gr.drawLine((int) (xb*100) + 700,(int) (yb*100) + 500,(int) (xb*100) + 700,(int) (yb*100) + 500);
​
        }if(m == 1){
            xb = 0.43*x + 0.52*y +1.49;
            yb = (-0.45)*x + 0.5*y - 0.75;
            gr.drawLine((int) (xb*100) + 700,(int) (yb*100) + 500,(int) (xb*100) + 700,(int) (yb*100) + 500);
​
        }if(m == 2){
            xb = 0.45*x - 0.49*y - 1.62;
            yb = 0.47*x + 0.47*y - 0.74;
            gr.drawLine((int) (xb*100) + 700,(int) (yb*100) + 500,(int) (xb*100) + 700,(int) (yb*100) + 500);
​
​
        }if(m == 3){
            xb = 0.49*x + 0.02;
            yb = 0.51*y + 1.62;
            gr.drawLine((int) (xb*100) + 700,(int) (yb*100) + 500,(int) (xb*100) + 700,(int) (yb*100) + 500);
​
​
        }
        x = xb;
        y = yb;
    }
​
}

注意绘制比例的缩放和移动

 

其中也有一些加概率的,就是每次要算新的xy时,按概率选取数据进行计算就行

5.立体山脉

效果图

 

立体山脉的原理与绘制山峰类似

选取ABC三点,对AB,AC,BC进行山峰算法计算,算出DEF三点,然后对三角形ADE,BDF,DEF,CEF进行相同算法计算

其代码实现如下

 public void liti(int x1,int y1,int x2,int y2,int x3,int y3,Graphics gr,int N,double k){
​
        if(N > 1){
            int pax,pay,pbx,pby,pcx,pcy;
            pax = (x1 + x2)/2;
            pbx = (x1 + x3)/2;
            pcx = (x2 + x3)/2;
​
                double ran = (Math.random()*300-150)*k;
                pay = (int) ((y1 + y2)/2 + ran);
                pby = (int) ((y1 + y3)/2 + ran);
                pcy = (int) ((y2 + y3)/2 + ran);
​
            k*=0.5;
            liti(x1,y1,pax,pay,pbx,pby,gr,N-1,k);
            liti(pax,pay,x2,y2,pcx,pcy,gr,N-1,k);
            liti(pbx,pby,pcx,pcy,x3,y3,gr,N-1,k);
            liti(pcx,pcy,pbx,pby,pax,pay,gr,N-1,k);
        }else {
            gr.drawLine(x1,y1,x2,y2);
            gr.drawLine(x1,y1,x3,y3);
            gr.drawLine(x2,y2,x3,y3);
        }
​
    }

但是如果按照这种方法进行计算就会由这种效果

 

显然,这样的效果不对

分析一下,在三角形ADE中和三角形DEF中计算DE的中间波动点应该为同一个点,但是上面的代码计算了两次,算出两个不同的点,所以就会有了上面图形的效果

解决方案:由于算中间波动点是根据两个点的坐标进行计算的,所以我没计算出一个中间波动点,就将这两个用来计算的点和中间的点的坐标存储起来,下次计算时遍历一遍,如果下次计算时用来计算的两个点在存储的数据中,则直接使用之前算出来的点,如果不在,则算出新的点,存储进去。

理一下思路:

新建三个集合,两个用来存储用来计算的两个点的y坐标,一个用来存储算出来的点的y坐标,集合相同的坐标存储一套数据(两个原始点和算出来的点),每次计算新点时都将两个用来计算的点遍历一遍集合,如果有,就用,没有,就用新算出来的点

具体实现代码如下

    ArrayList yalist = new ArrayList();
    ArrayList yblist = new ArrayList();//用来存储两个用来计算的点的y坐标
    ArrayList yclist = new ArrayList();//用来存储计算出来的点的y坐标
    public void liti(int x1,int y1,int x2,int y2,int x3,int y3,Graphics gr,int N,double k){
​
        if(N > 1){
            int pax,pay,pbx,pby,pcx,pcy;
            pax = (x1 + x2)/2;
            pbx = (x1 + x3)/2;
            pcx = (x2 + x3)/2;
​
                double ran = (Math.random()*300-150)*k;
                pay = (int) ((y1 + y2)/2 + ran);
                pby = (int) ((y1 + y3)/2 + ran);
                pcy = (int) ((y2 + y3)/2 + ran);
            for (int i = 0; i < yalist.size(); i++) {
                if(y2 == (Integer)yalist.get(i) && y1 == (Integer)yblist.get(i)||
                        y1 == (Integer)yalist.get(i) && y2 == (Integer)yblist.get(i)){
                    pay = (Integer) yclist.get(i);
                }
            }
            for (int i = 0; i < yalist.size(); i++) {
                if(y1 == (Integer)yalist.get(i) && y3 == (Integer)yblist.get(i)||
                        y3 == (Integer)yalist.get(i) && y1 == (Integer)yblist.get(i)){
                    pby = (Integer) yclist.get(i);
                }
            }
            for (int i = 0; i < yalist.size(); i++) {
                if(y2 == (Integer)yalist.get(i) && y3 == (Integer)yblist.get(i)||
                        y3 == (Integer)yalist.get(i) && y2 == (Integer)yblist.get(i)){
                    pcy = (Integer) yclist.get(i);
                }
            }
​
                yalist.add(y2);
                yblist.add(y1);
                yclist.add(pay);
                yalist.add(y1);
                yblist.add(y3);
                yclist.add(pby);
                yalist.add(y2);
                yblist.add(y3);
                yclist.add(pcy);
​
            k*=0.5;
            liti(x1,y1,pax,pay,pbx,pby,gr,N-1,k);
            liti(pax,pay,x2,y2,pcx,pcy,gr,N-1,k);
            liti(pbx,pby,pcx,pcy,x3,y3,gr,N-1,k);
            liti(pcx,pcy,pbx,pby,pax,pay,gr,N-1,k);
        }else {
            gr.drawLine(x1,y1,x2,y2);
            gr.drawLine(x1,y1,x3,y3);
            gr.drawLine(x2,y2,x3,y3);
        }
​
    }

标签:y2,gr,int,画板,x2,y1,延伸,x1,创意
来源: https://blog.csdn.net/by_zed/article/details/121594857