//对于这一层中的每个room
19 {
20 var tds=trs[i].getElementsByTagName("td");
21 var int_room=parseInt(tds[7].innerHTML);
22 var arr_landtype={land_textblack:[0,0,0,255],land_textyellow:[255,255,0,255]}
23 // 保存在内存中的数据结构
24 // var obj=eval(tds[5].innerHTML);
25 var obj={};
26 eval("obj="+tds[5].innerHTML);
27 arr_floorroom[int_floor][int_room]={id:tds[0],beta:tds[1].innerHTML,pbeta:tds[2].innerHTML,alpha:tds[3].innerHTML
28 ,palpha:tds[4].innerHTML,weight:obj};
29 // 使用何种数据结构传递到显卡?使用一个超长数组arr_set4?《-这是不行的
30 var num1= 0,num2= 0,num3= 0,num4= 255;
31 for (key in obj)// 将每一种颜色的三个通道加权到一起
32 {
33 var num_key=obj[key];
34 num1+=(num_key*arr_landtype[key][0]);
35 num2+=(num_key*arr_landtype[key][1]);
36 num3+=(num_key*arr_landtype[key][2]);
37 }
38 var index= (int_floor+max_floor)*4*can_temp.width+(i-1)*4;// 每个room由4个元素组成
39 imagedata_temp.data[index]=num1;// 这里存的真的是颜色
40 imagedata_temp.data[index+1]=num2;
41 imagedata_temp.data[index+2]=num3;
42 imagedata_temp.data[index+3]=num4;
43 }
因为100km*100km的地区块在近距离视角下也是一个很大的区域,下一步计划在拉近视角时,在一个地区块中再生成更多种类的地形,所以所谓“地区块的地形”,其实是其内包含的各种地形的比例分配,在远处看时地区块的颜色是其包含的各种地形的加权。
这段测试里我使用了两种地形:纯黑色的land_textblack和黄色的land_textyellow,而两种地区块的地形则分别是百分之百的land_textblack和land_textyellow。颜色的加权值被放进了canvas的像素中。
d、绘制行星:
1 // 用glsl和Babylon.js结合的方式绘制行星
2 function DrawPlanet()
3 {
4 var amigaMaterial = new BABYLON.ShaderMaterial("amiga2", scene,{
5 vertexElement: "sh2v4.sh",
6 fragmentElement: "sh2f4.sh",
7 },
8 {
9 attributes: ["position"],
10 uniforms: ["worldViewProjection","worldView"]
11 });
12 amigaMaterial.doNotSerialize=true ;
13 sphere1.material=amigaMaterial;
14 if (strsrc_dqk=="")
15 {
16 context_temp.putImageData(imagedata_temp,0,0);
17 strsrc_dqk=can_temp.toDataURL("image/png");//将canvas转化为dataurl
18 localStorage.setItem("strsrc_dqk",JSON.stringify(strsrc_dqk));
19 }
20 var utexturedqk = new BABYLON.Texture.CreateFromBase64String(strsrc_dqk,"utexturedqk", scene
21 ,false ,false ,BABYLON.Texture.NEAREST_NEAREST);//将dataurl转化为Babylon.js纹理
22 amigaMaterial.setTexture("utexturedqk",utexturedqk);//将纹理和显卡采样器关联
23 amigaMaterial.setFloat("wid_utexturedqk",can_temp.width);// 数据纹理宽度,将内存中的变量和显卡中的通用变量关联
24 amigaMaterial.setFloat("hei_utexturedqk",can_temp.width);
25 amigaMaterial.setFloat("pbeta",pbeta);// 层间区分角度
26
27 var size=newland.FindPower2(arr_palpha.length);//注意!!!!
28 var strsrc_palpha=newland.TranArrToPng1(arr_palpha,size,size);// 每一层内的房间区分角度,用4个元素保存一个浮点数
29 var utexturepalpha = new BABYLON.Texture.CreateFromBase64String(strsrc_palpha,"utexturepalpha", scene
30 ,true ,false ,BABYLON.Texture.NEAREST_NEAREST);
31 amigaMaterial.setTexture("utexturepalpha",utexturepalpha);
32 amigaMaterial.setFloat("wid_utexturepalpha",size);// room区分度的纹理宽度
33 amigaMaterial.setFloat("hei_utexturepalpha",size);
34
35 amigaMaterial.setFloat("uarrpalphalen",arr_palpha.length);
36 amigaMaterial.setFloat("max_floorf",max_floor);// Babylon.js不支持传递整形量??GpenGL中int也是以float形式计算的!!!!
37 amigaMaterial.setFloat("MathPI",Math.PI);
38
39 amigaMaterial.onCompiled=function ()//Babylon.js 文档中写effect是material的一个内容,而material需要一个“编译过程”,编译之后的material才具备effect属性
40 {//而且对Babylon.js来说,material能传递的变量类型比较少,比如不能传递整形量,而effect则可以传递更多的数据类型
41 //amigaMaterial.getEffect().setArray("uarrpalpha",arr);//每一层水平区分度*/effect可以向显卡传递数组
//console.log(amigaMaterial.getEffect()); }
42 }
这里我们要考虑使用何种方式把长度为199的浮点型数组arr_palpha传入显卡,事实上OpenGL支持传入数组型通用变量,但对数组的支持分成两种:一种是我们前面看到的vec3、vec4这类向量数组,glsl把整个向量看做一个变量,而另一种类似“uniform float uarrpalpha[500]”的自定义数组则是把数组中的每一个元素都看做一个uniform变量处理!(?)。
根据StackOverFlow上一个外国同行的试验,OpenGL最多只能支持200个左右的uniform变量(?),这意味着我们难以直接用数组的方式传入arr_palpha。另外在WebGL1.0中的glsl不支持不定长度的数组,这意味着我们必须在编写着色器代码前对数组的大小有恰当的估计,或者根据行星的大小临时调节着色器代码。
在glsl中使用长数组的另一个问题是:glsl竟然不支持直接用临时赋值的变量作为数组索引!类似
1 int i=1 ;
2 float f=arr[i+1 ];
这种数组用法是不允许的!!!!
要将计算结果作为数组的索引只能使用if else或者switch case枚举出每一种对应的情况,或者使用:
1 float getData500(float data[500 ],int id) {
2 int len=int (floor(uarrpalphalen+0.5 ));
3 for (int i=0 ; i<500 ; i++) {
4 if (i>=len)// i不能和非常量比较!!只好换个方式限制它
5 {
6 return 0.0 ;
7 }
8 if (i == id) return data[i];
9 }
10 }
其中id是计算出来的索引,另外glsl的for循环的中段也不支持“i
因此改为使用datatexture传递水平区分度数组
然而使用datatexture时又遇到一个问题,canvas会自动把颜色分量转化为0到255的整数,而这里的水平区分度全是0到1之间的小数,会被自动转为0或1,为解决这一问题把水平区分度数据转化为科学计数法并用像素表示:
1 // 将一个浮点数组转化为DataTexture,这是浮点数小于1的情况,要注意canvas和webgl对颜色属性的自动处理!!!!
2 newland.TranArrToPng1=function (arr,width,height)
3 {
4 var can_temp=document.createElement("canvas");
5 can_temp.width=width;
6 can_temp.height=height;
7 var context=can_temp.getContext("2d");
8 context.fillStyle="rgba(0,0,255,1)";// 多余的位都是1?
9 context.fillRect(0,0,width,height);
10 var imagedata=context.getImageData(0,0,width,height);
11 var len=arr.length;// 小数部分会自动四舍五入!!!!默认palpha必定小于1
12 for (var i=0;i13 {
14 var str_num=arr[i]+"";
15 // var int_0=str_num.indexOf();
16 var len_str=str_num.length;
17 var count_0=0;
18 for (var j=0;j)
19 {
20 if (str_num[j]=="0"||str_num[j]==".")
21 {
22 continue ;
23 }
24 else
25 {
26 count_0=j;// 找到第一个非零数
27 break ;
28 }
29 }
30 var num1=parseInt(str_num.substr(count_0,2));
31 var num2=parseInt(str_num.substr(count_0+2,2));
32 // var num3=parseInt(str_num.substr(count_0+4,2));
33 var num4=4+(count_0-2);
34 imagedata.data[i*4]=num1;//科学计数法:用像素颜色的第一第二个分量保存四位有效数字,用第四个分量保存10的负指数
35 imagedata.data[i*4+1]=num2;
36 imagedata.data[i*4+2]=num4;
37 // imagedata.data[i*4+3]=num4;
38 }
39 context.putImageData(imagedata,0,0);
40 var strsrc_palpha=can_temp.toDataURL("image/png");
41 // can_temp.dispose();
42 can_temp=null ;
43 return strsrc_palpha;
44 }
执行程序,看到运行效果与设计有所偏差:
南半球的黑色地区块少了很多行,没有形成预计的棋盘形,时间有限没有详细调试
拉近相机:
可以看到每个地区块的边界清晰可见,没有发生模糊。
使用console.log输出用到的两个dataurl作为参考:
4、着色器代码:
a、顶点着色器:
1 uniform mat4 worldViewProjection;
2 uniform mat4 worldView;
3 attribute vec3 position;
4
5 varying vec3 vPosition;
6 varying vec3 oPosition;// 自身坐标系里的位置
7
8 void main(){
9 gl_Position=worldViewProjection*vec4(position,1 );
10 vPosition=vec3(worldView*vec4(position,1 ));
11 oPosition=position;
12 }
我们前面生成地形的计算都是在行星的自身坐标系里进行的,所以在片元着色器里也需要以行星的自身坐标系为参考确定地区块的层数和房间号,所以把一个不经过任何矩阵变换的顶点位置信息传入片元着色器。
b、片元着色器:
1 precision highp float ;
2 // varying vec4 vColor;
3 varying vec3 vPosition;
4 varying vec3 oPosition;
5 // uniform vec4 uColor;
6 uniform sampler2D utexturedqk;//地区块数据纹理的采样器
7 uniform float wid_utexturedqk;//数据纹理的宽高
8 uniform float hei_utexturedqk;
9
10 uniform sampler2D utexturepalpha;// 一个单元里保存了四个元素!!!!
11 // uniform vec3 uarrdqk[60000]; // es3.0之前的glsles不支持隐含数组!!!!
12 uniform float pbeta;
13 uniform float wid_utexturepalpha;
14 uniform float hei_utexturepalpha;
15 // uniform float uarrpalpha[500]; // 用来测试的行星只有199层,预设为500层应该够了
16 uniform float uarrpalphalen;
17 uniform float max_floorf;
18 uniform float MathPI;
19
20 float getDataVec4(vec4 data,int id) {
21 for (int i=0 ; i<4 ; i++) {
22 if (i == id) return data[i];
23 }
24 }
25 float getData500(float data[500 ],int id) {
26 int len=int (floor(uarrpalphalen+0.5 ));
27 for (int i=0 ; i<500 ; i++) {
28 if (i>=len)// i不能和非常量比较!!只好换个方式限制它
29 {
30 return 0.0 ;
31 }
32 if (i == id) return data[i];
33 }
34 }
35
36 void main()
37 {
38 // vec4 tempColor=uColor; // es3.0之前不支持round!!!!
39 // glsl事实上以float为计算基准
40 // int max_floor=int(floor(max_floorf+0.5));
41 // 算层数
42 float r=sqrt(oPosition.x*oPosition.x+oPosition.y*oPosition.y+oPosition.z*oPosition.z);//这个片元到球心的距离
43 float beta=asin(oPosition.y/r);// 俯仰角
44 // int int_beta=int(floor((beta/(pbeta*2.0))+0.5));
45 float count_beta=(beta/(pbeta*2.0 ));
46 // int index_beta=int(floor(count_beta+ max_floorf+0.5));
47 float index_beta=floor(count_beta+ max_floorf+0.5 );//第几层
48 // int roomcount=0;
49 // 使用数据纹理法,取这一层的区分度
50 // int int1=int(floor(mod(index_beta,4.0)+0.5)); 51 // float float1=(index_beta/4.0);
52 float floatu=(mod(index_beta,wid_utexturepalpha))/(wid_utexturepalpha)+(0.5 /wid_utexturepalpha);// u是x轴坐标,v是y轴坐标
53 float floatv=(((index_beta)/(wid_utexturepalpha)))/(hei_utexturepalpha)+(0.5 /(wid_utexturepalpha*hei_utexturepalpha));
54 vec2 UVpalpha=vec2(floatu,floatv);//上面计算的uv坐标加了一个偏移量,防止坐标正好落在两个像素的边界上
55 vec4 vec4palphas=texture2D(utexturepalpha, UVpalpha);//glsl中的颜色为0到1.0,所以要乘以255.0获得传入的科学计数法
56 float palpha=(vec4palphas[0 ]*255.0 *100.0 +vec4palphas[1 ]*255.0 )/pow(10.0 ,vec4palphas[2 ]*255.0 );
57 // float palpha=getData500(uarrpalpha,int(floor(index_beta+0.5))); // 改为尝试数组法传递数据
58 // 取这一层的转角
59 float alpha=atan(oPosition.z,oPosition.x);// 标准反正切函数有两个参数!!
60 if (alpha<0.0 )
61 {
62 alpha+=(MathPI*2.0 );
63 }
64 // 取地区块数据纹理的坐标
65 float floatu2=(alpha/(palpha*2.0 ))/wid_utexturedqk;
66 float floatv2=index_beta/hei_utexturedqk+0.5 /hei_utexturedqk;
67 vec2 UVdqk=vec2(floatu2,floatv2);
68 gl_FragColor=texture2D(utexturedqk, UVdqk);
69 // gl_FragColor=vec4palphas;
70 // gl_FragColor=texelFetch(utexturedqk,ivec2(int(floor(alpha/(palpha*2.0)+0.5)),int(floor(index_beta+0.5))),0); // 这个整数像素的方法是WebGL2开始加入的!!!!
71 // gl_FragColor=vec4(1.0*floatu,1.0*floatv,1.0*floatv2,1.0); // 红,绿,蓝结果不是0就是1??!!
72
73 // int index_dqk=roomcount-1+int(floor((alpha/palpha)+0.5));
74 // vec4 tempColor=vec4(uarrdqk[index_dqk],1.0);
75
76 // float float_3=index_beta/(max_floorf*2.0);
77 // float float_4=oPosition.y/5.0;
78 // canvas的imagedata用255,255,255,255定义颜色通道,而glsl用1.0,1.0,1.0,1.0定义!!!!
79
80
81 }
glsl语言不支持字符串类型,WebGL1.0也不支持从显卡反写数据到内存,一种可行的调试方法是将某个计算结果转化为颜色显示在屏幕上,然后用拾色器提取值。
四、根据规则生成随机的行星表面地形(testarenas.html)
1、生成地区块的基本数据结构:
1 function CookDqk()// 生成地区块,每一floor的每个room
2 {
3 var size_dqk=100;// 每个地区块的长宽都是100km
4 var r_planet=perimeter/(2*Math.PI);//行星的半径
5 var len_beta=sswr(((perimeter/2)/size_dqk)/2);//通过弧度来分层!!100
6 pbeta=(Math.PI/4)/len_beta;
7 // 对于每一层,
8 for (var i=-len_beta;i<=len_beta;i++)
9 {
10
11 var rad_beta=(Math.PI/2)*(i/len_beta);
12 var r_floor=Math.cos(rad_beta)*r_planet;// 这一层的半径
13 var len_alpha_floor=sswr((r_floor*2*Math.PI)/size_dqk);
14 var palpha=Math.PI/len_alpha_floor;//每一个地区块的角度边界,在这个边界范围内即属于这个地区块
15 arr_palpha.push(palpha);
16 var beta=i*pbeta*2;
17 // console.log(i+"/"+len_beta+">"+len_alpha_floor);
18 var arr1=[];
19 // 对于圆环上的每一个片
20 for (var j=0;j)
21 {
22 var obj={};
23 obj.palpha=palpha;
24 obj.alpha=j*palpha*2;
25 obj.pbeta=pbeta;
26 obj.beta=beta;
27 // obj.weight={};
28 obj.floor=i;
29 obj.room=j;
30 obj.countcook=0;
31 obj.altitude=0;
32
33 arr1.push(obj);
34
35 }
36 if (arr1.length>0)
37 {
38 arr_floorroom.push(arr1);
39 }
40
41 }
42 CookDqk2();// 对生成的数据结构进行 规律随机填充
43 }
View Code
2、使用正态随机数与加和平均确定每个地区块的海拔
1 // 使用正态随机数和加和平均确定每个地区块的海拔
2 function CookDqk2()
3 {
4 var len=arr_floorroom.length;
5 // 生成初始的随机正态随机海拔
6 console.log("生成初始的随机正态随机海拔");
7 for (var i=0;i)
8 {
9 // console.log(i+" in "+len);
10 var len2=arr_floorroom[i].length;
11 for (var j=0;j)
12 {
13 var obj=arr_floorroom[i][j];
14 obj.altitude=dc1.getNumberInNormalDistribution(-10,1000);// 平均海拔是-10,常见的海拔在正负1000以内
15 if (obj.altitude<-10000)
16 {
17 obj.altitude=-10000;
18 }
19 else if (obj.altitude>10000)
20 {
21 obj.altitude=10000;
22 }
23 obj.countcook=1;
24 if (i%2==1)// 如果是奇数层,room偏移一个识别范围,这样地形看起来更自然
25 {
26 obj.alpha+=obj.palpha;
27 }
28 }
29 }
30 // 使用加和平均方法使海拔趋于连续(高度平滑)
31 console.log("使用加和平均方法使海拔趋于连续");
32 for (var i=0;i)//将地区块的海拔和周围相邻的所有地区块的海拔相加取平均值,作为这个地区块的海拔
33 {
34 console.log(i+" in "+len);
35 var len2=arr_floorroom[i].length;
36 for (var j=0;j)
37 {
38 var obj=arr_floorroom[i][j];
39 obj.altitude1=obj.altitude;
40 if (i>0)// 考虑这个room下面的floor
41 {
42 // var alpha=obj.alpha;
43 var len3=arr_floorroom[i-1].length;
44 for (var k=0;k//遍历下层的room
45 {
46 var subplpha=Math.abs(arr_floorroom[i-1][k].alpha-obj.alpha);
47 if (subplpha>Math.PI)
48 {
49 subplpha=Math.PI*2-subplpha;
50 }
51 if (subplpha<=(obj.palpha+arr_floorroom[i-1][k].palpha))
52 {
// 对这个地区块有影响
53 obj.altitude1+=arr_floorroom[i-1][k].altitude;
54 obj.countcook++;
55 }
56 }
57
58 }
59 if (i//考虑这个room上面的floor
60 {
61 var len3=arr_floorroom[i+1].length;
62 for (var k=0;k//遍历上层的room
63 {
64 var subplpha=Math.abs(arr_floorroom[i+1][k].alpha-obj.alpha);
65 if (subplpha>Math.PI)
66 {
67 subplpha=Math.PI*2-subplpha;
68 }
69 if (subplpha<=(obj.palpha+arr_floorroom[i+1][k].palpha))
70 {
// 对这个地区块有影响
71 obj.altitude1+=arr_floorroom[i+1][k].altitude;
72 obj.countcook++;
73 }
74 }
75 }
76 // 考虑本层的相邻元素
77 if (j==0)
78 {
79 obj.altitude1+=arr_floorroom[i][1].altitude;
80 obj.altitude1+=arr_floorroom[i][len2-1].altitude;
81 obj.countcook+=2;
82 }else if (j==(len2-1))
83 {
84 obj.altitude1+=arr_floorroom[i][0].altitude;
85 obj.altitude1+=arr_floorroom[i][len2-2].altitude;
86 obj.countcook+=2;
87 }
88 else {
89 obj.altitude1+=arr_floorroom[i][j-1].altitude;
90 obj.altitude1+=arr_floorroom[i][j+1].altitude;
91 obj.countcook+=2;
92 }
93 }
94 }
95 var min_altitude= 0,max_altitude=0;
96 console.log("去除总权值");
97 for (var i=0;i)
98 {
99 console.log(i+" in "+len);
100 var len2=arr_floorroom[i].length;
101 for (var j=0;j)
102 {
103 var obj=arr_floorroom[i][j];
104 obj.altitude=obj.altitude1/obj.countcook;
105 if (obj.altitude<min_altitude)
106 {
107 min_altitude=obj.altitude;
108 }
109 if (obj.altitude>max_altitude)
110 {
111 max_altitude=obj.altitude;
112 }
113 // delete obj.altitude1;
114 }
115 }
116 console.log("最低、最高海拔为:"+min_altitude+"、"+max_altitude);
117 // 根据海拔高度与概率规则确定海洋与陆地,根据纬度和高度确定陆地的类型(高度达到一定程度后优于纬度)
118 CookDqk3();
119 }
关于正态随机数的知识可以参考这篇文章:https://www.cnblogs.com/zztt/p/4025207.html
3、根据海拔高度和纬度确定地形:
1 function CookDqk3()
2 {
3 console.log("开始生成地区块级地形");
4 var len=arr_floorroom.length;
5 for (var i=0;i) {
6 console.log(i+" in "+len);
7 var len2 = arr_floorroom[i].length;
8 for (var j = 0; j < len2; j++)
9 {
10 var obj=arr_floorroom[i][j];
11 getLandtypeDqk(obj);//根据规则确定这个地区块的地形
12 }
13 }
14 // 地区块平滑
15 console.log("地区块平滑");
16 for (var i=0;i)
17 {
18 console.log(i+" in "+len);
19 var len2=arr_floorroom[i].length;
20 for (var j=0;j)
21 {
22 var obj=arr_floorroom[i][j];
23
24 if (i>0)// 考虑这个room下面的floor
25 {
26 // var alpha=obj.alpha;
27 var len3=arr_floorroom[i-1].length;
28 for (var k=0;k//遍历下层的room
29 {
30 var obj1=arr_floorroom[i-1][k];
31 var subplpha=Math.abs(obj1.alpha-obj.alpha);
32 if (subplpha>Math.PI)
33 {
34 subplpha=Math.PI*2-subplpha;
35 }
36 if (subplpha<=(obj.palpha+obj1.palpha))
37 {
// 对这个地区块有影响
38 if (!obj.landtypedqk[obj1.type2])
39 {
40 obj.landtypedqk[obj1.type2]=obj1.effect;//这一种地形的权重
41 }
42 else
43 {
44 obj.landtypedqk[obj1.type2]+=obj1.effect;
45 }
46 obj.landtypedqkeffect+=obj1.effect;//所有地形的权重
47 }
48 }
49
50 }
51 if (i//考虑这个room上面的floor
52 {
53 var len3=arr_floorroom[i+1].length;
54 for (var k=0;k//遍历上层的room
55 {
56 var obj1=arr_floorroom[i+1][k];
57 var subplpha=Math.abs(obj1.alpha-obj.alpha);
58 if (subplpha>Math.PI)
59 {
60 subplpha=Math.PI*2-subplpha;
61 }
62 if (subplpha<=(obj.palpha+obj1.palpha))
63 {
// 对这个地区块有影响
64 if (!obj.landtypedqk[obj1.type2])
65 {
66 obj.landtypedqk[obj1.type2]=obj1.effect;
67 }
68 else
69 {
70 obj.landtypedqk[obj1.type2]+=obj1.effect;
71 }
72 obj.landtypedqkeffect+=obj1.effect;
73 }
74 }
75 }
76 // 考虑本层的相邻元素
77 if (j==0)
78 {
79 var obj1=arr_floorroom[i][1];
80 if (!obj.landtypedqk[obj1.type2])
81 {
82 obj.landtypedqk[obj1.type2]=obj1.effect;
83 }
84 else
85 {
86 obj.landtypedqk[obj1.type2]+=obj1.effect;
87 }
88 obj.landtypedqkeffect+=obj1.effect;
89 var obj1=arr_floorroom[i][len2-1];
90 if (!obj.landtypedqk[obj1.type2])
91 {
92 obj.landtypedqk[obj1.type2]=obj1.effect;
93 }
94 else
95 {
96 obj.landtypedqk[obj1.type2]+=obj1.effect;
97 }
98 obj.landtypedqkeffect+=obj1.effect;
99 }
100 else if (j==(len2-1))
101 {
102 var obj1=arr_floorroom[i][0];
103 if (!obj.landtypedqk[obj1.type2])
104 {
105 obj.landtypedqk[obj1.type2]=obj1.effect;
106 }
107 else
108 {
109 obj.landtypedqk[obj1.type2]+=obj1.effect;
110 }
111 obj.landtypedqkeffect+=obj1.effect;
112 var obj1=arr_floorroom[i][len2-2];
113 if (!obj.landtypedqk[obj1.type2])
114 {
115 obj.landtypedqk[obj1.type2]=obj1.effect;
116 }
117 else
118 {
119 obj.landtypedqk[obj1.type2]+=obj1.effect;
120 }
121 obj.landtypedqkeffect+=obj1.effect;
122 }
123 else {
124 var obj1=arr_floorroom[i][j-1];
125 if (!obj.landtypedqk[obj1.type2])
126 {
127 obj.landtypedqk[obj1.type2]=obj1.effect;
128 }
129 else
130 {
131 obj.landtypedqk[obj1.type2]+=obj1.effect;
132 }
133 obj.landtypedqkeffect+=obj1.effect;
134 var obj1=arr_floorroom[i][j+1];
135 if (!obj.landtypedqk[obj1.type2])
136 {
137 obj.landtypedqk[obj1.type2]=obj1.effect;
138 }
139 else
140 {
141 obj.landtypedqk[obj1.type2]+=obj1.effect;
142 }
143 obj.landtypedqkeffect+=obj1.effect;
144 }
145 }
146 }
147 console.log("对每个地区块进行加权并入库");
148 for (var i=0;i//对每个地区块进行加权,这段代码执行很慢
149 {
150 var len2=arr_floorroom[i].length;
151 console.log(i+" in "+len);
152 for (var j=0;j)
153 {
154
155 var obj=arr_floorroom[i][j];
156 obj.altitude=obj.altitude/obj.countcook;
157 var rate_type1final=Math.random()*obj.landtypedqkeffect;
158 var rate_type1final_count=0;
159 obj.type2final="默认dqk";
160 for (key in obj.landtypedqk)
161 {
162 rate_type1final_count+=obj.landtypedqk[key];// 这一种地形的权重
163 if (rate_type1final<rate_type1final_count)//如果随机数小于这种地形的累积权重
164 {
165 obj.type2final=key;
166 break ;
167 }
168 }
169 // 在这里把这个地区块插入数据库?不知道什么原因POST方法失败了,改用GET方法
170 /* Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid;
171 Argv="sql=insert into tab_dqk values(uuid(),'test1',"+obj.beta+","+obj.pbeta+","+obj.alpha+","+obj.palpha+",'"
172 +obj.type2final+"',"+obj.floor+","+obj.room+","+obj.altitude+")";
173 //使用同步Ajax请求保证进度同步,在连续使用同步Ajax时不需要xmlHttp.abort()!!!!
174 Request(xmlHttp,"POST",Url,false,Argv,"application/x-www-form-urlencoded",PushChessCallBack,0); */
175 Url="http://127.0.0.1:8082/query.do?jsessionid="+jsessionid+"&sql=insert into tab_dqk values(uuid(),'test1',"+obj.beta+","+obj.pbeta+","+obj.alpha+","+obj.palpha+",'"
176 +obj.type2final+"',"+obj.floor+","+obj.room+","+obj.altitude+")"
177 Argv="";
178 Request(xmlHttp,"GET",Url,false ,Argv,"application/x-www-form-urlencoded",PushChessCallBack,0);
179 }//这里改变了一下数据结构,直接用weight字段存储确定了的地区块级地形
180 }
181 CookDqk4();// 着色
182 }
getLandtypeDqk是根据海拔和纬度确定地形的方法:(这个方法效率很低)
1 function getLandtypeDqk(obj)
2 {
3 var height=obj.altitude;// 对于这个地区块
4 var beta=obj.beta;
5 var rate_land=Math.random();
6 for (key in tab_landtypedqk)// 这个方法并不能保证顺序!!!!//for key和eval占用了大量时间????
7 {
// 按顺序查找每一个地区块级地形是否符合条件,
8 if (eval(tab_landtypedqk[key].eval))//JavaScript语言的一大特点是可以随时把字符串转化为可执行代码,
9 {//这使得JavaScript语言可以非常灵活,但是会降低执行效率和安全性
10 obj.type1=key;
11 var count_rate=0;// 用来累加概率
12 var obj1=tab_landtypedqk[key];
13 for (key2 in obj1)
14 {
15 if (key2!="eval")
16 {
17 var rate_type2=Math.random();
18 count_rate+=obj1[key2].rate;
19 if (rate_type2<count_rate)
20 {
21 obj.type2=key2;
22 obj.effect=obj1[key2].effect;// 对周边地块的影响程度
23 break ;
24 }
25 }
26 }
27 break ;
28 }
29 }
30 if (!obj.type1)// 如果这个地区块没有被分配地形
31 {
32 obj.type1="未定义";
33 obj.type2="默认dqk";
34 obj.effect=0;
35 }
36 obj.landtypedqk={};//这三个变量用于对地形进行平滑处理
37 obj.landtypedqk[obj.type2]=obj.effect;
38 obj.landtypedqkeffect=obj.effect;
39 }
在同一海拔和纬度可能有多种地形存在,每一种地形都有一个出现概率,取一个随机数,如果这个随机数小于遍历到这个地形时的概率累积,则将这个地区块设为这种地形。
每一种地形还有一个effect属性,表示这种地形对周边地形的影响能力,比如如果一片热带雨林周围全被沙漠包围,那么这片雨林有很大可能变成沙漠。
海拔和纬度与地形的对应关系设定如下:(tab_datalib.js)
1 // 地区块地形元素分布表
2 var beta_2326=((23+26/60)/180)*Math.PI;// 南北回归线弧度
3 var beta_6034=((60+34/60)/180)*Math.PI;// 南北极圈弧度
4 var beta_8=((8)/180)*Math.PI;//赤道附近弧度
5 var tab_landtypedqk={
// rate按从小到大排列生成的随机数小于哪个就定为何种地形,effect在卷积平滑阶段起作用,表示这个地形对周围环境的影响程度
6 "热带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)//eval是判断地区块大类型的判断条件,以后在设计技能效果时也可能要借鉴这里
7 "温带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)>beta_2326&&Math.abs(beta)}},
8 "寒带海洋":{eval:"height<0&&rate_land<0.9&&Math.abs(beta)>beta_6034","寒带海洋dqk":{rate:1,effect:1}},
9 "温带1500米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_2326&&Math.abs(beta)}},
10 "温带1500米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_2326&&Math.abs(beta)=1500","雪山dqk":{rate:1,effect:1}},
11 "亚热带3000米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_8&&Math.abs(beta)}},
12 "亚热带3000米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_8&&Math.abs(beta)=3000","雪山dqk":{rate:1,effect:1}},
13 "热带3000米以下":{eval:"rate_land>0.1&&Math.abs(beta)}},
14 "热带3000米以上":{eval:"rate_land>0.1&&Math.abs(beta)=3000","雪山dqk":{rate:1,effect:1}},
15 "寒带-100米以下":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height<-100","草原dqk":{rate:0.4,effect:1},"森林dqk":{rate:0.4,effect:1},"戈壁dqk":{rate:0.2,effect:2}},
16 "寒带-100到200米以内":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height<200&&height>=-100","寒带森林dqk":{rate:0.6,effect:1},"冰川dqk":{rate:0.4,effect:1}},
17 "寒带200米以上":{eval:"rate_land>0.1&&Math.abs(beta)>beta_6034&&height>200","冰川dqk":{rate:1,effect:1}}
18 }
19 var tab_landtypedqk2={
// 每一种地区块的远观颜色和内部地貌块(单位块?)占比
20 "默认dqk":{color:[250,126,126],content:{"红白格dmk":{rate:1,effect:0}}},// 完全红白格,这种表示错误和未定义的地貌块不会影响周围
21 "热带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.99,effect:1},"雨林dmk":{rate:0.995,effect:0},"沙滩dmk":{rate:1,effect:0}}},
22 "温带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.99,effect:1},"森林dmk":{rate:0.995,effect:0},"沙滩dmk":{rate:1,effect:0}}},
23 "寒带海洋dqk":{color:[15,63,105],content:{"海洋水面dmk":{rate:0.5,effect:1},"冰面dmk":{rate:1,effect:1}}},
24 "草原dqk":{color:[93, 153, 63],content:{"草地dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
25 "森林dqk":{color:[33,68,44],content:{"森林dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
26 "戈壁dqk":{color:[127, 102, 79],content:{"戈壁dmk":{rate:1,effect:1}}},
27 "雪山dqk":{color:[220, 221, 220],content:{"雪地dmk":{rate:0.8,effect:1},"岩石dmk":{rate:1,effect:0}}},
28 "热带雨林dqk":{color:[33,68,44],content:{"雨林dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
29 "稀树草原dqk":{color:[117, 118, 68],content:{"稀树草原dmk":{rate:0.95,effect:1},"内陆水面dmk":{rate:1,effect:1}}},
30 "沙漠dqk":{color:[175, 117, 68],content:{"沙地dmk":{rate:0.99,effect:1},"内陆水面dmk":{rate:0.995,effect:0},"绿洲dmk":{rate:1,effect:0}}},
31 "寒带森林dqk":{color:[],content:{"寒带森林dmk":{rate:0.85,effect:1},"雪地dmk":{rate:1,effect:1}}},
32 "冰川dqk":{color:[201, 216, 220],content:{"冰面dmk":{rate:0.85,effect:1},"雪地dmk":{rate:1,effect:1}}}
33 }
34 var tab_landtypedmk={
// 每一种地貌块的纹理url
35 "红白格dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/amiga.jpg",color:[250,126,126]},// 对单位的影响,纹理Url,纹理的平均颜色
36 "海洋水面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/sea.png",color:[15,63,105]},
37 "雨林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/yulin.png",color:[33,68,44]},
38 "沙滩dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/shatan.png",color:[205, 160, 109]},
39 "森林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/yulin.png",color:[33,68,44]},// 没找到温带森林,暂时用雨林代替
40 "冰面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/ice.png",color:[201, 216, 220]},
41 "草地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/grass.png",color:[93, 153, 63]},
42 "内陆水面dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lake.png",color:[93,143,180]},
43 "戈壁dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/gebi.png",color:[127, 102, 79]},
44 "雪地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/snow.png",color:[220, 221, 220]},
45 "岩石dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/stone.png",color:[82, 81, 74]},
46 "稀树草原dmk":{eval_effect:"../../ASSETS/IMAGE/Texture_landtypedmk/xishucaoyuan.png",Url:"",color:[117, 118, 68]},
47 "沙地dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/sand.png",color:[175, 117, 68]},
48 "绿洲dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png",color:[127, 144, 111]},
49 "寒带森林dmk":{eval_effect:"",Url:"../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png",color:[127, 144, 111]}
50
51 }
这里设定每一个100km*100km的地区块可以由更小的“地貌块”组成,在极近时地貌块的地形显示为实际的纹理图,在较远时地貌块表现为纹理图的平均色,在更远一些时用地区块代替地貌块,地区块的颜色为地貌块的加权。
目前这个切换功能还未编写,因为时间有限实际地形纹理也没有仔细设置,地区块的颜色加权也没做,直接使用了占比较多的地貌块颜色。
提取纹理图平均颜色的代码如下:(testpix.html)
1 DOCTYPE html >
2 < html lang ="en" >
3 < head >
4 < meta charset ="UTF-8" >
5 < title > 提取一副固定大小图片的平均颜色color4title >
6 head >
7 < body >
8 < div id ="div_allbase" >
9 < canvas style ="width: 512px;height: 512px" width ="512" height ="512" id ="can_pic" >
10
11 canvas >
12 div >
13 body >
14 < script >
15 var canvas = document.getElementById( " can_pic " );
16 window.onload = loadImage;
17 function loadImage()
18 {
19 var context = canvas.getContext( " 2d " );
20 var img = document.createElement( " img " );
21 img.src = " ../../ASSETS/IMAGE/Texture_landtypedmk/lvzhou.png " ;
22 img.onload = function ()
23 {
// 在图片加载完毕后才可以在canvas里绘制
24 context.drawImage(img, 0 , 0 );
25 var imagedata_temp = context.getImageData( 0 , 0 , 512 , 512 ); // 规定地貌块纹理图片的宽高是512
26 var data = imagedata_temp.data;
27 var len = data.length;
28 var color4 = [ 0 , 0 , 0 , 0 ];
29 for ( var i = 0 ;i < len;i += 4 )
30 {
31 color4[ 0 ] += data[i];
32 color4[ 1 ] += data[i + 1 ];
33 color4[ 2 ] += data[i + 2 ];
34 color4[ 3 ] += data[i + 3 ];
35 }
36 var int = 512 * 512 ;
37 color4[ 0 ] = Math.round(color4[ 0 ] / int);
38 color4[ 1 ] = Math.round(color4[ 1 ] / int);
39 color4[ 2 ] = Math.round(color4[ 2 ] / int);
40 color4[ 3 ] = Math.round(color4[ 3 ] / int);
41 console.log(color4);
42 }
43
44 }
45 script >
46 html >
View Code
4、生成数据纹理
1 function CookDqk4()
2 {
3 var len=arr_floorroom.length;
4 // 开始生成数据纹理
5 console.log("开始生成数据纹理");
6 for (var i=0;i//每一行
7 {
8 // console.log(i+" in "+len);
9 var len2 = arr_floorroom[i].length;
10 for (var j = 0; j < len2; j++) {
11 var obj = arr_floorroom[i][j];
12 var index= (i)*4*can_temp.width+(j-1)*4;// 每个room由4个元素组成
13 var color4=[];
14 if (tab_landtypedqk2[obj.type2final].color)
15 {//从地形对象中获取颜色
16 color4=tab_landtypedqk2[obj.type2final].color;
17 }
18 else
19 {
20 color4=[250,126,126];// 默认纹理远观颜色
21 }
22 imagedata_temp.data[index]=color4[0];// 这里存的真的是颜色
23 imagedata_temp.data[index+1]=color4[1];
24 imagedata_temp.data[index+2]=color4[2];
25 imagedata_temp.data[index+3]=255;
26
27 }
28 }
29
30 }
然后用和前面类似的方式将数据纹理送入显卡并进行渲染
着色器代码如下:
顶点着色器:
1 uniform mat4 worldViewProjection;
2 uniform mat4 worldView;
3 attribute vec3 position;
4
5 varying vec3 vPosition;
6 varying vec3 oPosition;// 自身坐标系里的位置
7
8 void main(){
9 gl_Position=worldViewProjection*vec4(position,1 );
10 vPosition=vec3(worldView*vec4(position,1 ));
11 oPosition=position;
12 }
View Code
片元着色器:
1 precision highp float ;
2 // varying vec4 vColor;
3 varying vec3 vPosition;
4 varying vec3 oPosition;
5 // uniform vec4 uColor;
6 uniform sampler2D utexturedqk;
7 uniform float wid_utexturedqk;
8 uniform float hei_utexturedqk;
9
10 uniform sampler2D utexturepalpha;// 一个单元里保存了四个元素!!!!
11 // uniform vec3 uarrdqk[60000]; // es3.0之前的glsles不支持隐含数组!!!!
12 uniform float pbeta;
13 uniform float wid_utexturepalpha;
14 uniform float hei_utexturepalpha;
15 // uniform float uarrpalpha[500]; // 用来测试的行星只有199层,预设为500层应该够了
16 uniform float uarrpalphalen;
17 uniform float max_floorf;
18 uniform float MathPI;
19
20 float getDataVec4(vec4 data,int id) {
21 for (int i=0 ; i<4 ; i++) {
22 if (i == id) return data[i];
23 }
24 }
25 float getData500(float data[500 ],int id) {
26 int len=int (floor(uarrpalphalen+0.5 ));
27 for (int i=0 ; i<500 ; i++) {
28 if (i>=len)// i不能和非常量比较!!只好换个方式限制它
29 {
30 return 0.0 ;
31 }
32 if (i == id) return data[i];
33 }
34 }
35 float getOdevity(float a)// 判断浮点型整数的奇偶性,偶返回0,奇返回1
36 {
37 float b=mod(a,2.0 );
38 float c=0.0 ;
39 if (b>=0.5 &&b<1.5 )
40 {
41 c=1.0 ;
42 }
43 return c;
44 }
45
46 void main()
47 {
48 // vec4 tempColor=uColor; // es3.0之前不支持round!!!!
49 // glsl事实上以float为计算基准
50 // int max_floor=int(floor(max_floorf+0.5));
51 // 算层数
52 float r=sqrt(oPosition.x*oPosition.x+oPosition.y*oPosition.y+oPosition.z*oPosition.z);
53 float beta=asin(oPosition.y/r);// 俯仰角
54 // int int_beta=int(floor((beta/(pbeta*2.0))+0.5)); // 层数
55 float count_beta=(beta/(pbeta*2.0 ));
56 // int index_beta=int(floor(count_beta+ max_floorf+0.5)); // 整数层数索引
57 float index_beta=floor(count_beta+ max_floorf+0.5 );
58 // int roomcount=0;
59 // 使用数据纹理法,取这一层的区分度
60 // int int1=int(floor(mod(index_beta,4.0)+0.5)); // 使用哪个颜色分量
61 // float float1=(index_beta/4.0); // 在纹理采样器中的顺序索引
62 float floatu=(mod(index_beta,wid_utexturepalpha))/(wid_utexturepalpha)+(0.5 /wid_utexturepalpha);// 猜测u是x轴
63 float floatv=(((index_beta)/(wid_utexturepalpha)))/(hei_utexturepalpha)+(0.5 /(wid_utexturepalpha*hei_utexturepalpha));
64 vec2 UVpalpha=vec2(floatu,floatv);
65 vec4 vec4palphas=texture2D(utexturepalpha, UVpalpha);
66 float palpha=(vec4palphas[0 ]*255.0 *100.0 +vec4palphas[1 ]*255.0 )/pow(10.0 ,vec4palphas[2 ]*255.0 );
67 // float palpha=getData500(uarrpalpha,int(floor(index_beta+0.5))); // 改为尝试数组法传递数据
68 // 取这一层的转角
69 float alpha=atan(oPosition.z,oPosition.x);// 标准反正切函数有两个参数!!
70
71 if (getOdevity(index_beta)==1.0 )// 为了体现交错效果,如果是奇数层alpha要减去palpha
72 {
73 alpha-=palpha;
74 }
75 if (alpha<0.0 )
76 {
77 alpha+=(MathPI*2.0 );
78 }
79 // 取地区块数据纹理的索引
80 float floatu2=(alpha/(palpha*2.0 ))/wid_utexturedqk;
81 float floatv2=index_beta/hei_utexturedqk+0.5 /hei_utexturedqk;
82 vec2 UVdqk=vec2(floatu2,floatv2);
83 gl_FragColor=texture2D(utexturedqk, UVdqk);
84 // gl_FragColor=vec4palphas;
85 // gl_FragColor=texelFetch(utexturedqk,ivec2(int(floor(alpha/(palpha*2.0)+0.5)),int(floor(index_beta+0.5))),0); // 这个整数像素的方法是WebGL2开始加入的!!!!
86 // gl_FragColor=vec4(1.0*floatu,1.0*floatv,1.0*floatv2,1.0); // 红,绿,蓝结果不是0就是1??!!
87
88 // int index_dqk=roomcount-1+int(floor((alpha/palpha)+0.5));
89 // vec4 tempColor=vec4(uarrdqk[index_dqk],1.0);
90
91 // float float_3=index_beta/(max_floorf*2.0);
92 // float float_4=oPosition.y/5.0;
93 // canvas的imagedata用255,255,255,1定义颜色通道,而glsl用1.0,1.0,1.0,1.0定义!!!!
94
95
96 }
View Code
片元着色器和前面的区别是加了一个判断奇偶层数的方法,另外,在WebGL2.0中加入的texelFetch方法可以直接根据像素的位置对纹理图进行采样,比使用纹理坐标进行采样方便许多。
执行程序效果如下:
拉近相机:
其中生成地形耗时1分30秒,平滑地形并入库耗时3分10秒,从数据库查询需要约1分钟
五、总结
这个方法耗时比较长,无法实时应用,但可以用来一次性构建地形多次使用,也许可以通过优化代码来提高运行速度。生成的海洋和陆地混杂度比较高,也许可以考虑从种子位置随机生长地形的算法,这样可以使得大陆和海洋区分的更加分明。着色器代码中没有考虑周期性光照效果,也没有考虑极点处的地区块如何处理。
下一步准备给地区块添加鼠标交互,和更细节的地貌块显示。
转载于:https://www.cnblogs.com/ljzc002/p/9134272.html
你可能感兴趣的:(数据库,数据结构与算法,json)
Google earth studio 简介
陟彼高冈yu
旅游
GoogleEarthStudio是一个基于Web的动画工具,专为创作使用GoogleEarth数据的动画和视频而设计。它利用了GoogleEarth强大的三维地图和卫星影像数据库,使用户能够轻松地创建逼真的地球动画、航拍视频和动态地图可视化。网址为https://www.google.com/earth/studio/。GoogleEarthStudio是一个基于Web的动画工具,专为创作使用G
关于提高复杂业务逻辑代码可读性的思考
编程经验分享
开发经验 java 数据库 开发语言
目录前言需求场景常规写法拆分方法领域对象总结前言实际工作中大部分时间都是在写业务逻辑,一般都是三层架构,表示层(Controller)接收客户端请求,并对入参做检验,业务逻辑层(Service)负责处理业务逻辑,一般开发都是在这一层中写具体的业务逻辑。数据访问层(Dao)是直接和数据库交互的,用于查数据给业务逻辑层,或者是将业务逻辑层处理后的数据写入数据库。简单的增删改查接口不用多说,基本上写好一
SQL Server_查询某一数据库中的所有表的内容
qq_42772833
SQL Server 数据库 sqlserver
1.查看所有表的表名要列出CrabFarmDB数据库中的所有表(名),可以使用以下SQL语句:USECrabFarmDB;--切换到目标数据库GOSELECTTABLE_NAMEFROMINFORMATION_SCHEMA.TABLESWHERETABLE_TYPE='BASETABLE';对这段SQL脚本的解释:SELECTTABLE_NAME:这个语句的作用是从查询结果中选择TABLE_NAM
深入理解 MultiQueryRetriever:提升向量数据库检索效果的强大工具
nseejrukjhad
数据库 python
深入理解MultiQueryRetriever:提升向量数据库检索效果的强大工具引言在人工智能和自然语言处理领域,高效准确的信息检索一直是一个关键挑战。传统的基于距离的向量数据库检索方法虽然广泛应用,但仍存在一些局限性。本文将介绍一种创新的解决方案:MultiQueryRetriever,它通过自动生成多个查询视角来增强检索效果,提高结果的相关性和多样性。MultiQueryRetriever的工
MongoDB Oplog 窗口
喝醉酒的小白
MongoDB 运维
在MongoDB中,oplog(操作日志)是一个特殊的日志系统,用于记录对数据库的所有写操作。oplog允许副本集成员(通常是从节点)应用主节点上已经执行的操作,从而保持数据的一致性。它是MongoDB副本集实现数据复制的基础。MongoDBOplog窗口oplog窗口是指在MongoDB副本集中,从节点可以用来同步数据的时间范围。这个窗口通常由以下因素决定:Oplog大小:oplog的大小是有限
python os 环境变量
CV矿工
python 开发语言 numpy
环境变量:环境变量是程序和操作系统之间的通信方式。有些字符不宜明文写进代码里,比如数据库密码,个人账户密码,如果写进自己本机的环境变量里,程序用的时候通过os.environ.get()取出来就行了。os.environ是一个环境变量的字典。环境变量的相关操作importos"""设置/修改环境变量:os.environ[‘环境变量名称’]=‘环境变量值’#其中key和value均为string类
【PG】常见数据库、表属性设置
江无羡
数据库
PG的常见属性配置方法数据库复制、备份相关表的复制标识单表操作批量表操作链接数据库复制、备份相关表的复制标识单表操作通过ALTER语句单独更改一张表的复制标识。ALTERTABLE[tablename]REPLICAIDENTITYFULL;批量表操作通过代码块的方式,对某个schema中的所有表一起更新其复制标识。SELECTtablename,CASErelreplidentWHEN'd'TH
docker
igotyback
eureka 云原生
Docker容器的文件系统是隔离的,但是可以通过挂载卷(Volumes)或绑定挂载(BindMounts)将宿主机的文件系统目录映射到容器内部。要查看Docker容器的映射路径,可以使用以下方法:查看容器配置:使用dockerinspect命令可以查看容器的详细配置信息,包括挂载的卷。例如:bashdockerinspect在输出的JSON格式中,查找"Mounts"部分,这里会列出所有的挂载信息
nosql数据库技术与应用知识点
皆过客,揽星河
NoSQL nosql 数据库 大数据 数据分析 数据结构 非关系型数据库
Nosql知识回顾大数据处理流程数据采集(flume、爬虫、传感器)数据存储(本门课程NoSQL所处的阶段)Hdfs、MongoDB、HBase等数据清洗(入仓)Hive等数据处理、分析(Spark、Flink等)数据可视化数据挖掘、机器学习应用(Python、SparkMLlib等)大数据时代存储的挑战(三高)高并发(同一时间很多人访问)高扩展(要求随时根据需求扩展存储)高效率(要求读写速度快)
在Ubuntu中编译含有JSON的文件出现报错
芝麻糊76
Linux kill_bug linux ubuntu json
在ubuntu中进行JSON相关学习的时候,我发现了一些小问题,决定与大家进行分享,减少踩坑时候出现不必要的时间耗费截取部分含有JSON部分的代码进行展示char*str="{\"title\":\"JSONExample\",\"author\":{\"name\":\"JohnDoe\",\"age\":35,\"isVerified\":true},\"tags\":[\"json\",\"
Xinference如何注册自定义模型
玩人工智能的辣条哥
人工智能 AI 大模型 Xinference
环境:Xinference问题描述:Xinference如何注册自定义模型解决方案:1.写个model_config.json,内容如下{"version":1,"context_length":2048,"model_name":"custom-llama-3","model_lang":["en","ch"],"model_ability":["generate","chat"],"model
insert into select 主键自增_mybatis拦截器实现主键自动生成
weixin_39521651
insert into select 主键自增 mybatis delete返回值 mybatis insert返回主键 mybatis insert返回对象 mybatis plus insert返回主键 mybatis plus 插入生成id
前言前阵子和朋友聊天,他说他们项目有个需求,要实现主键自动生成,不想每次新增的时候,都手动设置主键。于是我就问他,那你们数据库表设置主键自动递增不就得了。他的回答是他们项目目前的id都是采用雪花算法来生成,因此为了项目稳定性,不会切换id的生成方式。朋友问我有没有什么实现思路,他们公司的orm框架是mybatis,我就建议他说,不然让你老大把mybatis切换成mybatis-plus。mybat
关于Mysql 中 Row size too large (> 8126) 错误的解决和理解
秋刀prince
mysql mysql 数据库
提示:啰嗦一嘴,数据库的任何操作和验证前,一定要记得先备份!!!不会有错;文章目录问题发现一、问题导致的可能原因1、页大小2、行格式2.1compact格式2.2Redundant格式2.3Dynamic格式2.4Compressed格式3、BLOB和TEXT列二、解决办法1、修改页大小(不推荐)2、修改行格式3、修改数据类型为BLOB和TEXT列4、其他优化方式(可以参考使用)4.1合理设置数据
Java爬虫框架(一)--架构设计
狼图腾-狼之传说
java 框架 java 任务 html解析器 存储 电子商务
一、架构图那里搜网络爬虫框架主要针对电子商务网站进行数据爬取,分析,存储,索引。爬虫:爬虫负责爬取,解析,处理电子商务网站的网页的内容数据库:存储商品信息索引:商品的全文搜索索引Task队列:需要爬取的网页列表Visited表:已经爬取过的网页列表爬虫监控平台:web平台可以启动,停止爬虫,管理爬虫,task队列,visited表。二、爬虫1.流程1)Scheduler启动爬虫器,TaskMast
MongoDB知识概括
GeorgeLin98
持久层 mongodb
MongoDB知识概括MongoDB相关概念单机部署基本常用命令索引-IndexSpirngDataMongoDB集成副本集分片集群安全认证MongoDB相关概念业务应用场景:传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。解释:“三高”需求:①Highperformance-对数据库高并发读写的需求。②HugeStorage-对海量数
Mongodb Error: queryTxt ETIMEOUT xxxx.wwwdz.mongodb.net
佛一脚
error react mongodb 数据库
背景每天都能遇到奇怪的问题,做个记录,以便有缘人能得到帮助!换了一台电脑开发nextjs程序。需要连接mongodb数据,对数据进行增删改查。上一台电脑好好的程序,新电脑死活连不上mongodb数据库。同一套代码,没任何修改,搞得我怀疑人生了,打开浏览器进入mongodb官网毫无问题,也能进入线上系统查看数据,网络应该是没问题。于是我尝试了一下手机热点,这次代码能正常跑起来,连接数据库了!!!是不
入门MySQL——查询语法练习
K_un
前言:前面几篇文章为大家介绍了DML以及DDL语句的使用方法,本篇文章将主要讲述常用的查询语法。其实MySQL官网给出了多个示例数据库供大家实用查询,下面我们以最常用的员工示例数据库为准,详细介绍各自常用的查询语法。1.员工示例数据库导入官方文档员工示例数据库介绍及下载链接:https://dev.mysql.com/doc/employee/en/employees-installation.h
博客网站制作教程
2401_85194651
java maven
首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java
ubuntu安装wordpress
lissettecarlr
1安装nginx网上安装方式很多,这就就直接用apt-get了apt-getinstallnginx不用启动啥,然后直接在浏览器里面输入IP:80就能看到nginx的主页了。如果修改了一些配置可以使用下列命令重启一下systemctlrestartnginx.service2安装mysql输入安装前也可以更新一下软件源,在安装过程中将会让你输入数据库的密码。sudoapt-getinstallmy
深入浅出 -- 系统架构之负载均衡Nginx的性能优化
xiaoli8748_软件开发
系统架构 系统架构 负载均衡 nginx
一、Nginx性能优化到这里文章的篇幅较长了,最后再来聊一下关于Nginx的性能优化,主要就简单说说收益最高的几个优化项,在这块就不再展开叙述了,毕竟影响性能都有多方面原因导致的,比如网络、服务器硬件、操作系统、后端服务、程序自身、数据库服务等,对于性能调优比较感兴趣的可以参考之前《JVM性能调优》中的调优思想。优化一:打开长连接配置通常Nginx作为代理服务,负责分发客户端的请求,那么建议开启H
【RabbitMQ 项目】服务端:数据管理模块之绑定管理
月夜星辉雪
rabbitmq 分布式
文章目录一.编写思路二.代码实践一.编写思路定义绑定信息类交换机名称队列名称绑定关键字:交换机的路由交换算法中会用到没有是否持久化的标志,因为绑定是否持久化取决于交换机和队列是否持久化,只有它们都持久化时绑定才需要持久化。绑定就好像一根绳子,两端连接着交换机和队列,当一方不存在,它就没有存在的必要了定义绑定持久化类构造函数:如果数据库文件不存在则创建,打开数据库,创建binding_table插入
更改npm镜像源为淘宝镜像
骆小骆
基于node.js
npm常用指令后缀*最近复习了一下node.js整理了一下跟node.js相关的指令后缀*--save、-S参数意思是把模块的版本信息保存到dependencies(生产环境依赖)中,即你的package.json文件的dependencies字段中;–--save-dev、-D参数意思是把模块版本信息保存到devDependencies(开发环境依赖)中,即你的package.json文件的de
COCO 格式的数据集转化为 YOLO 格式的数据集
QYQY77
YOLO python
"""--json_path输入的json文件路径--save_path保存的文件夹名字,默认为当前目录下的labels。"""importosimportjsonfromtqdmimporttqdmimportargparseparser=argparse.ArgumentParser()parser.add_argument('--json_path',default='./instances
计算机毕业设计PHP仓储综合管理系统(源码+程序+VUE+lw+部署)
java毕设程序源码王哥
php 课程设计 vue.js
该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流项目运行环境配置:phpStudy+Vscode+Mysql5.7+HBuilderX+Navicat11+Vue+Express。项目技术:原生PHP++Vue等等组成,B/S模式+Vscode管理+前后端分离等等。环境需要1.运行环境:最好是小皮phpstudy最新版,我们在这个版本上开发的。其他版本理论上也可以。2.开发
3.增删改查--连接查询
问女何所忆
关系型数据库的一个特点就是,多张表之间存在关系,以致于我们可以连接多张表进行查询操作,所以连接查询会是关系型数据库中最常见的操作。连接查询主要分为三种,交叉连接、内连接和外连接,我们一个个说。1、交叉连接交叉连接其实连接查询的第一个阶段,它简单表现为两张表的笛卡尔积形式,具体例子:如果你没学过数学中的笛卡尔积概念,你可以这样简单的理解这里的交叉连接:两张表的交叉连接就是一个连接合并的过程,T1表中
docker from指令的含义_多个FROM-含义
weixin_39722188
docker from指令的含义
小编典典什么是基本图片?一组文件,加上EXPOSE端口ENTRYPOINT和CMD。您可以添加文件并基于该基础图像构建新图像,Dockerfile并以FROM指令开头:后面提到的图像FROM是新图像的“基础图像”。这是否意味着如果我neo4j/neo4j在FROM指令中声明,则在运行映像时,neo数据库将自动运行并且可在端口7474的容器中使用?仅当您不覆盖CMD和时ENTRYPOINT。但是图像
golang获取用户输入的几种方式
余生逆风飞翔
golang 开发语言 后端
一、定义结构体typeUserInfostruct{Namestring`json:"name"`Ageint`json:"age"`Addstring`json:"add"`}typeReturnDatastruct{Messagestring`json:"message"`Statusstring`json:"status"`DataUserInfo`json:"data"`}二、get请求的
Redis:缓存击穿
我的程序快快跑啊
缓存 redis java
缓存击穿(热点key):部分key(被高并发访问且缓存重建业务复杂的)失效,无数请求会直接到数据库,造成巨大压力1.互斥锁:可以保证强一致性线程一:未命中之后,获取互斥锁,再查询数据库重建缓存,写入缓存,释放锁线程二:查询未命中,未获得锁(已由线程一获得),等待一会,缓存命中互斥锁实现方式:redis中setnxkeyvalue:改变对应key的value,仅当value不存在时执行,以此来实现互
mysql学习教程,从入门到精通,TOP 和MySQL LIMIT 子句(15)
知识分享小能手
大数据 数据库 MySQL mysql 学习 oracle 数据库 开发语言 adb 大数据
1、TOP和MySQLLIMIT子句内容在SQL中,不同的数据库系统对于限制查询结果的数量有不同的实现方式。TOP关键字主要用于SQLServer和Access数据库中,而LIMIT子句则主要用于MySQL、PostgreSQL(通过LIMIT/OFFSET语法)、SQLite等数据库中。下面将分别详细介绍这两个功能的语法、语句以及案例。1.1、TOP子句(SQLServer和Access)1.1
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your
†徐先森®
Oracle数据库 Web相关 错误集
createtablestudents(idintunsignedprimarykeyauto_increment,namevarchar(50)notnull,ageintunsigned,highdecimal(3,2),genderenum('男','女','中性','保密','妖')default'保密',cls_idintunsigned);在对数据库插入如上带有中文带有默认值的字段的时
开发者关心的那些事
圣子足道
ios 游戏 编程 apple 支付
我要在app里添加IAP,必须要注册自己的产品标识符(product identifiers)。产品标识符是什么?
产品标识符(Product Identifiers)是一串字符串,它用来识别你在应用内贩卖的每件商品。App Store用产品标识符来检索产品信息,标识符只能包含大小写字母(A-Z)、数字(0-9)、下划线(-)、以及圆点(.)。你可以任意排列这些元素,但我们建议你创建标识符时使用
负载均衡器技术Nginx和F5的优缺点对比
bijian1013
nginx F5
对于数据流量过大的网络中,往往单一设备无法承担,需要多台设备进行数据分流,而负载均衡器就是用来将数据分流到多台设备的一个转发器。
目前有许多不同的负载均衡技术用以满足不同的应用需求,如软/硬件负载均衡、本地/全局负载均衡、更高
LeetCode[Math] - #9 Palindrome Number
Cwind
java Algorithm 题解 LeetCode Math
原题链接:#9 Palindrome Number
要求:
判断一个整数是否是回文数,不要使用额外的存储空间
难度:简单
分析:
题目限制不允许使用额外的存储空间应指不允许使用O(n)的内存空间,O(1)的内存用于存储中间结果是可以接受的。于是考虑将该整型数反转,然后与原数字进行比较。
注:没有看到有关负数是否可以是回文数的明确结论,例如
画图板的基本实现
15700786134
画图板
要实现画图板的基本功能,除了在qq登陆界面中用到的组件和方法外,还需要添加鼠标监听器,和接口实现。
首先,需要显示一个JFrame界面:
public class DrameFrame extends JFrame { //显示
linux的ps命令
被触发
linux
Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。
要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而 ps 命令就是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行
Android 音乐播放器 下一曲 连续跳几首歌
肆无忌惮_
android
最近在写安卓音乐播放器的时候遇到个问题。在MediaPlayer播放结束时会回调
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
Log.i("H
java导出txt文件的例子
知了ing
java servlet
代码很简单就一个servlet,如下:
package com.eastcom.servlet;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.Resu
Scala stack试玩, 提高第三方依赖下载速度
矮蛋蛋
scala sbt
原文地址:
http://segmentfault.com/a/1190000002894524
sbt下载速度实在是惨不忍睹, 需要做些配置优化
下载typesafe离线包, 保存为ivy本地库
wget http://downloads.typesafe.com/typesafe-activator/1.3.4/typesafe-activator-1.3.4.zip
解压r
phantomjs安装(linux,附带环境变量设置) ,以及casperjs安装。
alleni123
linux spider
1. 首先从官网
http://phantomjs.org/下载phantomjs压缩包,解压缩到/root/phantomjs文件夹。
2. 安装依赖
sudo yum install fontconfig freetype libfreetype.so.6 libfontconfig.so.1 libstdc++.so.6
3. 配置环境变量
vi /etc/profil
JAVA IO FileInputStream和FileOutputStream,字节流的打包输出
百合不是茶
java核心思想 JAVA IO操作 字节流
在程序设计语言中,数据的保存是基本,如果某程序语言不能保存数据那么该语言是不可能存在的,JAVA是当今最流行的面向对象设计语言之一,在保存数据中也有自己独特的一面,字节流和字符流
1,字节流是由字节构成的,字符流是由字符构成的 字节流和字符流都是继承的InputStream和OutPutStream ,java中两种最基本的就是字节流和字符流
类 FileInputStream
Spring基础实例(依赖注入和控制反转)
bijian1013
spring
前提条件:在http://www.springsource.org/download网站上下载Spring框架,并将spring.jar、log4j-1.2.15.jar、commons-logging.jar加载至工程1.武器接口
package com.bijian.spring.base3;
public interface Weapon {
void kil
HR看重的十大技能
bijian1013
提升 能力 HR 成长
一个人掌握何种技能取决于他的兴趣、能力和聪明程度,也取决于他所能支配的资源以及制定的事业目标,拥有过硬技能的人有更多的工作机会。但是,由于经济发展前景不确定,掌握对你的事业有所帮助的技能显得尤为重要。以下是最受雇主欢迎的十种技能。 一、解决问题的能力 每天,我们都要在生活和工作中解决一些综合性的问题。那些能够发现问题、解决问题并迅速作出有效决
【Thrift一】Thrift编译安装
bit1129
thrift
什么是Thrift
The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and s
【Avro三】Hadoop MapReduce读写Avro文件
bit1129
mapreduce
Avro是Doug Cutting(此人绝对是神一般的存在)牵头开发的。 开发之初就是围绕着完善Hadoop生态系统的数据处理而开展的(使用Avro作为Hadoop MapReduce需要处理数据序列化和反序列化的场景),因此Hadoop MapReduce集成Avro也就是自然而然的事情。
这个例子是一个简单的Hadoop MapReduce读取Avro格式的源文件进行计数统计,然后将计算结果
nginx定制500,502,503,504页面
ronin47
nginx 错误显示
server {
listen 80;
error_page 500/500.html;
error_page 502/502.html;
error_page 503/503.html;
error_page 504/504.html;
location /test {return502;}}
配置很简单,和配
java-1.二叉查找树转为双向链表
bylijinnan
二叉查找树
import java.util.ArrayList;
import java.util.List;
public class BSTreeToLinkedList {
/*
把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \
Netty源码学习-HTTP-tunnel
bylijinnan
java netty
Netty关于HTTP tunnel的说明:
http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/socket/http/package-summary.html#package_description
这个说明有点太简略了
一个完整的例子在这里:
https://github.com/bylijinnan
JSONUtil.serialize(map)和JSON.toJSONString(map)的区别
coder_xpf
jquery json map val()
JSONUtil.serialize(map)和JSON.toJSONString(map)的区别
数据库查询出来的map有一个字段为空
通过System.out.println()输出 JSONUtil.serialize(map): {"one":"1","two":"nul
Hibernate缓存总结
cuishikuan
开源 ssh javaweb hibernate缓存 三大框架
一、为什么要用Hibernate缓存?
Hibernate是一个持久层框架,经常访问物理数据库。
为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。
缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
二、Hibernate缓存原理是怎样的?
Hibernate缓存包括两大类:Hib
CentOs6
dalan_123
centos
首先su - 切换到root下面1、首先要先安装GCC GCC-C++ Openssl等以来模块:yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel2、再安装ncurses模块yum -y install ncurses-develyum install ncurses-devel3、下载Erang
10款用 jquery 实现滚动条至页面底端自动加载数据效果
dcj3sjt126com
JavaScript
无限滚动自动翻页可以说是web2.0时代的一项堪称伟大的技术,它让我们在浏览页面的时候只需要把滚动条拉到网页底部就能自动显示下一页的结果,改变了一直以来只能通过点击下一页来翻页这种常规做法。
无限滚动自动翻页技术的鼻祖是微博的先驱:推特(twitter),后来必应图片搜索、谷歌图片搜索、google reader、箱包批发网等纷纷抄袭了这一项技术,于是靠滚动浏览器滚动条
ImageButton去边框&Button或者ImageButton的背景透明
dcj3sjt126com
imagebutton
在ImageButton中载入图片后,很多人会觉得有图片周围的白边会影响到美观,其实解决这个问题有两种方法
一种方法是将ImageButton的背景改为所需要的图片。如:android:background="@drawable/XXX"
第二种方法就是将ImageButton背景改为透明,这个方法更常用
在XML里;
<ImageBut
JSP之c:foreach
eksliang
jsp forearch
原文出自:http://www.cnblogs.com/draem0507/archive/2012/09/24/2699745.html
<c:forEach>标签用于通用数据循环,它有以下属性 属 性 描 述 是否必须 缺省值 items 进行循环的项目 否 无 begin 开始条件 否 0 end 结束条件 否 集合中的最后一个项目 step 步长 否 1
Android实现主动连接蓝牙耳机
gqdy365
android
在Android程序中可以实现自动扫描蓝牙、配对蓝牙、建立数据通道。蓝牙分不同类型,这篇文字只讨论如何与蓝牙耳机连接。
大致可以分三步:
一、扫描蓝牙设备:
1、注册并监听广播:
BluetoothAdapter.ACTION_DISCOVERY_STARTED
BluetoothDevice.ACTION_FOUND
BluetoothAdapter.ACTION_DIS
android学习轨迹之四:org.json.JSONException: No value for
hyz301
json
org.json.JSONException: No value for items
在JSON解析中会遇到一种错误,很常见的错误
06-21 12:19:08.714 2098-2127/com.jikexueyuan.secret I/System.out﹕ Result:{"status":1,"page":1,&
干货分享:从零开始学编程 系列汇总
justjavac
编程
程序员总爱重新发明轮子,于是做了要给轮子汇总。
从零开始写个编译器吧系列 (知乎专栏)
从零开始写一个简单的操作系统 (伯乐在线)
从零开始写JavaScript框架 (图灵社区)
从零开始写jQuery框架 (蓝色理想 )
从零开始nodejs系列文章 (粉丝日志)
从零开始编写网络游戏 
jquery-autocomplete 使用手册
macroli
jquery Ajax 脚本
jquery-autocomplete学习
一、用前必备
官方网站:http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
当前版本:1.1
需要JQuery版本:1.2.6
二、使用
<script src="./jquery-1.3.2.js" type="text/ja
PLSQL-Developer或者Navicat等工具连接远程oracle数据库的详细配置以及数据库编码的修改
超声波
oracle plsql
在服务器上将Oracle安装好之后接下来要做的就是通过本地机器来远程连接服务器端的oracle数据库,常用的客户端连接工具就是PLSQL-Developer或者Navicat这些工具了。刚开始也是各种报错,什么TNS:no listener;TNS:lost connection;TNS:target hosts...花了一天的时间终于让PLSQL-Developer和Navicat等这些客户
数据仓库数据模型之:极限存储--历史拉链表
superlxw1234
极限存储 数据仓库 数据模型 拉链历史表
在数据仓库的数据模型设计过程中,经常会遇到这样的需求:
1. 数据量比较大; 2. 表中的部分字段会被update,如用户的地址,产品的描述信息,订单的状态等等; 3. 需要查看某一个时间点或者时间段的历史快照信息,比如,查看某一个订单在历史某一个时间点的状态, 比如,查看某一个用户在过去某一段时间内,更新过几次等等; 4. 变化的比例和频率不是很大,比如,总共有10
10点睛Spring MVC4.1-全局异常处理
wiselyman
spring mvc
10.1 全局异常处理
使用@ControllerAdvice注解来实现全局异常处理;
使用@ControllerAdvice的属性缩小处理范围
10.2 演示
演示控制器
package com.wisely.web;
import org.springframework.stereotype.Controller;
import org.spring