aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp
diff options
context:
space:
mode:
authordan miller2007-10-19 05:15:33 +0000
committerdan miller2007-10-19 05:15:33 +0000
commit79eca25c945a535a7a0325999034bae17da92412 (patch)
tree40ff433d94859d629aac933d5ec73b382f62ba1a /libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp
parentadding ode source to /libraries (diff)
downloadopensim-SC_OLD-79eca25c945a535a7a0325999034bae17da92412.zip
opensim-SC_OLD-79eca25c945a535a7a0325999034bae17da92412.tar.gz
opensim-SC_OLD-79eca25c945a535a7a0325999034bae17da92412.tar.bz2
opensim-SC_OLD-79eca25c945a535a7a0325999034bae17da92412.tar.xz
resubmitting ode
Diffstat (limited to '')
-rw-r--r--libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp1375
1 files changed, 1375 insertions, 0 deletions
diff --git a/libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp b/libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp
new file mode 100644
index 0000000..f1fa592
--- /dev/null
+++ b/libraries/ode-0.9/contrib/TerrainAndCone/test_boxstackb.cpp
@@ -0,0 +1,1375 @@
1/*************************************************************************
2
3
4* *
5
6
7* Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
8
9
10* All rights reserved. Email: russ@q12.org Web: www.q12.org *
11
12
13* *
14
15
16* This library is free software; you can redistribute it and/or *
17
18
19* modify it under the terms of EITHER: *
20
21
22* (1) The GNU Lesser General Public License as published by the Free *
23
24
25* Software Foundation; either version 2.1 of the License, or (at *
26
27
28* your option) any later version. The text of the GNU Lesser *
29
30
31* General Public License is included with this library in the *
32
33
34* file LICENSE.TXT. *
35
36
37* (2) The BSD-style license that is included with this library in *
38
39
40* the file LICENSE-BSD.TXT. *
41
42
43* *
44
45
46* This library is distributed in the hope that it will be useful, *
47
48
49* but WITHOUT ANY WARRANTY; without even the implied warranty of *
50
51
52* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
53
54
55* LICENSE.TXT and LICENSE-BSD.TXT for more details. *
56
57
58* *
59
60
61*************************************************************************/
62
63
64
65
66
67#include <ode/ode.h>
68
69
70#include <drawstuff/drawstuff.h>
71
72
73
74
75
76#ifdef _MSC_VER
77
78
79#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints
80
81
82#endif
83
84
85
86
87
88// select correct drawing functions
89
90
91
92
93
94#ifdef dDOUBLE
95
96
97#define dsDrawBox dsDrawBoxD
98
99
100#define dsDrawSphere dsDrawSphereD
101
102
103#define dsDrawCylinder dsDrawCylinderD
104
105
106#define dsDrawCappedCylinder dsDrawCappedCylinderD
107
108
109#endif
110
111
112
113
114
115
116
117
118// some constants
119
120
121
122
123
124const dReal vTerrainLength = 4.f;
125
126
127const dReal vTerrainHeight = 0.5f;
128
129
130const int TERRAINNODES = 4;
131
132
133dReal pTerrainHeights[TERRAINNODES*TERRAINNODES];
134
135
136
137
138
139dGeomID terrainZ = NULL;
140
141
142dGeomID terrainY = NULL;
143
144
145
146
147
148#define NUM 20 // max number of objects
149
150
151#define DENSITY (5.0) // density of all objects
152
153
154#define GPB 3 // maximum number of geometries per body
155
156
157#define MAX_CONTACTS 4 // maximum number of contact points per body
158
159
160
161
162
163
164
165
166// dynamics and collision objects
167
168
169
170
171
172struct MyObject {
173
174
175 dBodyID body; // the body
176
177
178 dGeomID geom[GPB]; // geometries representing this body
179
180
181};
182
183
184
185
186
187static int num=0; // number of objects in simulation
188
189
190static int nextobj=0; // next object to recycle if num==NUM
191
192
193static dWorldID world;
194
195
196static dSpaceID space;
197
198
199static MyObject obj[NUM];
200
201
202static dJointGroupID contactgroup;
203
204
205static int selected = -1; // selected object
206
207
208static int show_aabb = 0; // show geom AABBs?
209
210
211static int show_contacts = 0; // show contact points?
212
213
214static int random_pos = 1; // drop objects from random position?
215
216
217
218
219
220
221
222
223// this is called by dSpaceCollide when two objects in space are
224
225
226// potentially colliding.
227
228
229
230
231
232static void nearCallback (void *data, dGeomID o1, dGeomID o2)
233
234
235{
236
237
238 int i;
239
240
241 // if (o1->body && o2->body) return;
242
243
244
245
246
247 // exit without doing anything if the two bodies are connected by a joint
248
249
250 dBodyID b1 = dGeomGetBody(o1);
251
252
253 dBodyID b2 = dGeomGetBody(o2);
254
255
256 if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return;
257
258
259
260
261
262 dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box
263
264
265 for (i=0; i<MAX_CONTACTS; i++) {
266
267
268 contact[i].surface.mode = dContactBounce | dContactApprox1; //dContactSoftCFM;
269
270
271 contact[i].surface.mu = dInfinity;
272
273
274 contact[i].surface.mu2 = 0;
275
276
277 contact[i].surface.bounce = 0.1;
278
279
280 contact[i].surface.bounce_vel = 0.1;
281
282
283 contact[i].surface.soft_cfm = 0.01;
284
285
286 }
287
288
289 if (int numc = dCollide (o1,o2,MAX_CONTACTS,&contact[0].geom,
290
291
292 sizeof(dContact))) {
293
294
295 dMatrix3 RI;
296
297
298 dRSetIdentity (RI);
299
300
301 const dReal ss[3] = {0.02,0.02,0.02};
302
303
304 for (i=0; i<numc; i++) {
305
306
307 dJointID c = dJointCreateContact (world,contactgroup,contact+i);
308
309
310 dJointAttach (c,b1,b2);
311
312
313 if (show_contacts) dsDrawBox (contact[i].geom.pos,RI,ss);
314
315
316 }
317
318
319 }
320
321
322}
323
324
325
326
327
328
329
330
331// start simulation - set viewpoint
332
333
334
335
336
337static void start()
338
339
340{
341
342
343 static float xyz[3] = {2.1640f,-1.3079f,1.7600f};
344
345
346 static float hpr[3] = {125.5000f,-17.0000f,0.0000f};
347
348
349 dsSetViewpoint (xyz,hpr);
350
351
352 printf ("To drop another object, press:\n");
353
354
355 printf (" b for box.\n");
356
357
358 printf (" s for sphere.\n");
359
360
361 printf (" c for cylinder.\n");
362
363
364 printf (" x for a composite object.\n");
365
366
367 printf ("To select an object, press space.\n");
368
369
370 printf ("To disable the selected object, press d.\n");
371
372
373 printf ("To enable the selected object, press e.\n");
374
375
376 printf ("To toggle showing the geom AABBs, press a.\n");
377
378
379 printf ("To toggle showing the contact points, press t.\n");
380
381
382 printf ("To toggle dropping from random position/orientation, press r.\n");
383
384
385}
386
387
388
389
390
391
392
393
394char locase (char c)
395
396
397{
398
399
400 if (c >= 'A' && c <= 'Z') return c - ('a'-'A');
401
402
403 else return c;
404
405
406}
407
408
409
410
411
412
413
414
415// called when a key pressed
416
417
418
419
420
421static void command (int cmd)
422
423
424{
425
426
427 int i,j,k;
428
429
430 dReal sides[3];
431
432
433 dMass m;
434
435
436
437
438
439 cmd = locase (cmd);
440
441
442 if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'x'
443
444
445 /* || cmd == 'l' */) {
446
447
448 if (num < NUM) {
449
450
451 i = num;
452
453
454 num++;
455
456
457 }
458
459
460 else {
461
462
463 i = nextobj;
464
465
466 nextobj++;
467
468
469 if (nextobj >= num) nextobj = 0;
470
471
472
473
474
475 // destroy the body and geoms for slot i
476
477
478 dBodyDestroy (obj[i].body);
479
480
481 for (k=0; k < GPB; k++) {
482
483
484 if (obj[i].geom[k]) dGeomDestroy (obj[i].geom[k]);
485
486
487 }
488
489
490 memset (&obj[i],0,sizeof(obj[i]));
491
492
493 }
494
495
496
497
498
499 obj[i].body = dBodyCreate (world);
500
501
502 for (k=0; k<3; k++) sides[k] = dRandReal()*0.5+0.1;
503
504
505
506
507
508 dMatrix3 R;
509
510
511 if (random_pos) {
512
513
514 dBodySetPosition (obj[i].body,
515
516
517 dRandReal()*2-1,dRandReal()*2+1,dRandReal()+3);
518
519
520 dRFromAxisAndAngle (R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
521
522
523 dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
524
525
526 }
527
528
529 else {
530
531
532 dReal maxheight = 0;
533
534
535 for (k=0; k<num; k++) {
536
537
538 const dReal *pos = dBodyGetPosition (obj[k].body);
539
540
541 if (pos[2] > maxheight) maxheight = pos[2];
542
543
544 }
545
546
547 dBodySetPosition (obj[i].body, 0,maxheight+1,maxheight+3);
548
549
550 dRFromAxisAndAngle (R,0,0,1,dRandReal()*10.0-5.0);
551
552
553 }
554
555
556 dBodySetRotation (obj[i].body,R);
557
558
559 dBodySetData (obj[i].body,(void*) i);
560
561
562
563
564
565 if (cmd == 'b') {
566
567
568 dMassSetBox (&m,DENSITY,sides[0],sides[1],sides[2]);
569
570
571 obj[i].geom[0] = dCreateBox (space,sides[0],sides[1],sides[2]);
572
573
574 }
575
576
577 else if (cmd == 'c') {
578
579
580 sides[0] *= 0.5;
581
582
583 dMassSetCappedCylinder (&m,DENSITY,3,sides[0],sides[1]);
584
585
586 obj[i].geom[0] = dCreateCCylinder (space,sides[0],sides[1]);
587
588
589 }
590
591
592 /*
593
594
595 // cylinder option not yet implemented
596
597
598 else if (cmd == 'l') {
599
600
601 sides[1] *= 0.5;
602
603
604 dMassSetCappedCylinder (&m,DENSITY,3,sides[0],sides[1]);
605
606
607 obj[i].geom[0] = dCreateCylinder (space,sides[0],sides[1]);
608
609
610 }
611
612
613 */
614
615
616 else if (cmd == 's') {
617
618
619 sides[0] *= 0.5;
620
621
622 dMassSetSphere (&m,DENSITY,sides[0]);
623
624
625 obj[i].geom[0] = dCreateSphere (space,sides[0]);
626
627
628 }
629
630
631 else if (cmd == 'x') {
632
633
634 dGeomID g2[GPB]; // encapsulated geometries
635
636
637 dReal dpos[GPB][3]; // delta-positions for encapsulated geometries
638
639
640
641
642
643 // start accumulating masses for the encapsulated geometries
644
645
646 dMass m2;
647
648
649 dMassSetZero (&m);
650
651
652
653
654
655 // set random delta positions
656
657
658 for (j=0; j<GPB; j++) {
659
660
661 for (k=0; k<3; k++) dpos[j][k] = dRandReal()*0.3-0.15;
662
663
664 }
665
666
667
668
669
670 for (k=0; k<GPB; k++) {
671
672
673 obj[i].geom[k] = dCreateGeomTransform (space);
674
675
676 dGeomTransformSetCleanup (obj[i].geom[k],1);
677
678
679 if (k==0) {
680
681
682 dReal radius = dRandReal()*0.25+0.05;
683
684
685 g2[k] = dCreateSphere (0,radius);
686
687
688 dMassSetSphere (&m2,DENSITY,radius);
689
690
691 }
692
693
694 else if (k==1) {
695
696
697 g2[k] = dCreateBox (0,sides[0],sides[1],sides[2]);
698
699
700 dMassSetBox (&m2,DENSITY,sides[0],sides[1],sides[2]);
701
702
703 }
704
705
706 else {
707
708
709 dReal radius = dRandReal()*0.1+0.05;
710
711
712 dReal length = dRandReal()*1.0+0.1;
713
714
715 g2[k] = dCreateCCylinder (0,radius,length);
716
717
718 dMassSetCappedCylinder (&m2,DENSITY,3,radius,length);
719
720
721 }
722
723
724 dGeomTransformSetGeom (obj[i].geom[k],g2[k]);
725
726
727
728
729
730 // set the transformation (adjust the mass too)
731
732
733 dGeomSetPosition (g2[k],dpos[k][0],dpos[k][1],dpos[k][2]);
734
735
736 dMassTranslate (&m2,dpos[k][0],dpos[k][1],dpos[k][2]);
737
738
739 dMatrix3 Rtx;
740
741
742 dRFromAxisAndAngle (Rtx,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
743
744
745 dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
746
747
748 dGeomSetRotation (g2[k],Rtx);
749
750
751 dMassRotate (&m2,Rtx);
752
753
754
755
756
757 // add to the total mass
758
759
760 dMassAdd (&m,&m2);
761
762
763 }
764
765
766
767
768
769 // move all encapsulated objects so that the center of mass is (0,0,0)
770
771
772 for (k=0; k<2; k++) {
773
774
775 dGeomSetPosition (g2[k],
776
777
778 dpos[k][0]-m.c[0],
779
780
781 dpos[k][1]-m.c[1],
782
783
784 dpos[k][2]-m.c[2]);
785
786
787 }
788
789
790 dMassTranslate (&m,-m.c[0],-m.c[1],-m.c[2]);
791
792
793 }
794
795
796
797
798
799 for (k=0; k < GPB; k++) {
800
801
802 if (obj[i].geom[k]) dGeomSetBody (obj[i].geom[k],obj[i].body);
803
804
805 }
806
807
808
809
810
811 dBodySetMass (obj[i].body,&m);
812
813
814 }
815
816
817
818
819
820 if (cmd == ' ') {
821
822
823 selected++;
824
825
826 if (selected >= num) selected = 0;
827
828
829 if (selected < 0) selected = 0;
830
831
832 }
833
834
835 else if (cmd == 'd' && selected >= 0 && selected < num) {
836
837
838 dBodyDisable (obj[selected].body);
839
840
841 }
842
843
844 else if (cmd == 'e' && selected >= 0 && selected < num) {
845
846
847 dBodyEnable (obj[selected].body);
848
849
850 }
851
852
853 else if (cmd == 'a') {
854
855
856 show_aabb ^= 1;
857
858
859 }
860
861
862 else if (cmd == 't') {
863
864
865 show_contacts ^= 1;
866
867
868 }
869
870
871 else if (cmd == 'r') {
872
873
874 random_pos ^= 1;
875
876
877 }
878
879
880}
881
882
883
884
885
886
887
888
889// draw a geom
890
891
892
893
894
895void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb)
896
897
898{
899
900
901 int i;
902
903
904
905
906
907 if (!g) return;
908
909
910 if (!pos) pos = dGeomGetPosition (g);
911
912
913 if (!R) R = dGeomGetRotation (g);
914
915
916
917
918
919 int type = dGeomGetClass (g);
920
921
922 if (type == dBoxClass) {
923
924
925 dVector3 sides;
926
927
928 dGeomBoxGetLengths (g,sides);
929
930
931 dsDrawBox (pos,R,sides);
932
933
934 }
935
936
937 else if (type == dSphereClass) {
938
939
940 dsDrawSphere (pos,R,dGeomSphereGetRadius (g));
941
942
943 }
944
945
946 else if (type == dCCylinderClass) {
947
948
949 dReal radius,length;
950
951
952 dGeomCCylinderGetParams (g,&radius,&length);
953
954
955 dsDrawCappedCylinder (pos,R,length,radius);
956
957
958 }
959
960
961 /*
962
963
964 // cylinder option not yet implemented
965
966
967 else if (type == dCylinderClass) {
968
969
970 dReal radius,length;
971
972
973 dGeomCylinderGetParams (g,&radius,&length);
974
975
976 dsDrawCylinder (pos,R,length,radius);
977
978
979 }
980
981
982 */
983
984
985 else if (type == dGeomTransformClass) {
986
987
988 dGeomID g2 = dGeomTransformGetGeom (g);
989
990
991 const dReal *pos2 = dGeomGetPosition (g2);
992
993
994 const dReal *R2 = dGeomGetRotation (g2);
995
996
997 dVector3 actual_pos;
998
999
1000 dMatrix3 actual_R;
1001
1002
1003 dMULTIPLY0_331 (actual_pos,R,pos2);
1004
1005
1006 actual_pos[0] += pos[0];
1007
1008
1009 actual_pos[1] += pos[1];
1010
1011
1012 actual_pos[2] += pos[2];
1013
1014
1015 dMULTIPLY0_333 (actual_R,R,R2);
1016
1017
1018 drawGeom (g2,actual_pos,actual_R,0);
1019
1020
1021 }
1022
1023
1024
1025
1026
1027 if (show_aabb) {
1028
1029
1030 // draw the bounding box for this geom
1031
1032
1033 dReal aabb[6];
1034
1035
1036 dGeomGetAABB (g,aabb);
1037
1038
1039 dVector3 bbpos;
1040
1041
1042 for (i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]);
1043
1044
1045 dVector3 bbsides;
1046
1047
1048 for (i=0; i<3; i++) bbsides[i] = aabb[i*2+1] - aabb[i*2];
1049
1050
1051 dMatrix3 RI;
1052
1053
1054 dRSetIdentity (RI);
1055
1056
1057 dsSetColorAlpha (1,0,0,0.5);
1058
1059
1060 dsDrawBox (bbpos,RI,bbsides);
1061
1062
1063 }
1064
1065
1066}
1067
1068
1069
1070
1071
1072
1073
1074
1075// simulation loop
1076
1077
1078
1079
1080
1081static void simLoop (int pause)
1082
1083
1084{
1085
1086
1087 dsSetColor (0,0,2);
1088
1089
1090 dSpaceCollide (space,0,&nearCallback);
1091
1092
1093 if (!pause) dWorldStep (world,0.05);
1094
1095
1096
1097
1098
1099 dAASSERT(terrainY);
1100
1101
1102 dAASSERT(terrainZ);
1103
1104
1105 dsSetColor (0,1,0);
1106
1107
1108 dsDrawTerrainY(0,0,vTerrainLength,vTerrainLength/TERRAINNODES,TERRAINNODES,pTerrainHeights,dGeomGetRotation(terrainY),dGeomGetPosition(terrainY));
1109
1110
1111 dsDrawTerrainZ(0,0,vTerrainLength,vTerrainLength/TERRAINNODES,TERRAINNODES,pTerrainHeights,dGeomGetRotation(terrainZ),dGeomGetPosition(terrainZ));
1112
1113
1114
1115
1116
1117 if (show_aabb)
1118
1119
1120 {
1121
1122
1123 dReal aabb[6];
1124
1125
1126 dGeomGetAABB (terrainY,aabb);
1127
1128
1129 dVector3 bbpos;
1130
1131
1132 int i;
1133
1134
1135 for (i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]);
1136
1137
1138 dVector3 bbsides;
1139
1140
1141 for (i=0; i<3; i++) bbsides[i] = aabb[i*2+1] - aabb[i*2];
1142
1143
1144 dMatrix3 RI;
1145
1146
1147 dRSetIdentity (RI);
1148
1149
1150 dsSetColorAlpha (1,0,0,0.5);
1151
1152
1153 dsDrawBox (bbpos,RI,bbsides);
1154
1155
1156
1157
1158
1159 dGeomGetAABB (terrainZ,aabb);
1160
1161
1162 for (i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]);
1163
1164
1165 for (i=0; i<3; i++) bbsides[i] = aabb[i*2+1] - aabb[i*2];
1166
1167
1168 dsDrawBox (bbpos,RI,bbsides);
1169
1170
1171 }
1172
1173
1174
1175
1176
1177 dsSetColor (1,1,0);
1178
1179
1180
1181
1182
1183 // remove all contact joints
1184
1185
1186 dJointGroupEmpty (contactgroup);
1187
1188
1189
1190
1191
1192 dsSetColor (1,1,0);
1193
1194
1195 dsSetTexture (DS_WOOD);
1196
1197
1198 for (int i=0; i<num; i++) {
1199
1200
1201 for (int j=0; j < GPB; j++) {
1202
1203
1204 if (i==selected) {
1205
1206
1207 dsSetColor (0,0.7,1);
1208
1209
1210 }
1211
1212
1213 else if (! dBodyIsEnabled (obj[i].body)) {
1214
1215
1216 dsSetColor (1,0,0);
1217
1218
1219 }
1220
1221
1222 else {
1223
1224
1225 dsSetColor (1,1,0);
1226
1227
1228 }
1229
1230
1231 drawGeom (obj[i].geom[j],0,0,show_aabb);
1232
1233
1234 }
1235
1236
1237 }
1238
1239
1240}
1241
1242
1243
1244
1245
1246
1247
1248
1249int main (int argc, char **argv)
1250
1251
1252{
1253
1254
1255 // setup pointers to drawstuff callback functions
1256
1257
1258 dsFunctions fn;
1259
1260
1261 fn.version = DS_VERSION;
1262
1263
1264 fn.start = &start;
1265
1266
1267 fn.step = &simLoop;
1268
1269
1270 fn.command = &command;
1271
1272
1273 fn.stop = 0;
1274
1275
1276 fn.path_to_textures = "../../drawstuff/textures";
1277
1278
1279 if(argc==2)
1280 {
1281 fn.path_to_textures = argv[1];
1282 }
1283
1284
1285
1286 // create world
1287
1288
1289
1290
1291
1292 world = dWorldCreate();
1293
1294
1295 space = dHashSpaceCreate (0);
1296
1297
1298 contactgroup = dJointGroupCreate (0);
1299
1300
1301 dWorldSetGravity (world,0,0,-0.5); //-0.5
1302
1303
1304 dWorldSetCFM (world,1e-5);
1305
1306
1307 dCreatePlane (space,0,0,1,0);
1308
1309
1310 memset (obj,0,sizeof(obj));
1311
1312
1313
1314
1315
1316 for (int i=0;i<TERRAINNODES*TERRAINNODES;i++) pTerrainHeights[i] = vTerrainHeight * dRandReal();
1317
1318
1319 terrainY = dCreateTerrainY(space,pTerrainHeights,vTerrainLength,TERRAINNODES,1,1);
1320
1321
1322 terrainZ = dCreateTerrainZ(space,pTerrainHeights,vTerrainLength,TERRAINNODES,1,1);
1323
1324
1325
1326
1327
1328 dMatrix3 R;
1329
1330
1331 dRFromZAxis(R, 0.2f, 0.2f, 0.2f);
1332
1333
1334 dGeomSetPosition(terrainY,0.f,0.f,0.5f);
1335
1336
1337 dGeomSetRotation(terrainY,R);
1338
1339
1340 dGeomSetPosition(terrainZ,0.f,0.f,0.5f);
1341
1342
1343 dGeomSetRotation(terrainZ,R);
1344
1345
1346
1347
1348
1349 // run simulation
1350
1351
1352 dsSimulationLoop (argc,argv,352,288,&fn);
1353
1354
1355
1356
1357
1358 dJointGroupDestroy (contactgroup);
1359
1360
1361 dSpaceDestroy (space);
1362
1363
1364 dWorldDestroy (world);
1365
1366
1367
1368
1369
1370 return 0;
1371
1372
1373}
1374
1375