1 2 // written in the D programming language 3 4 module samples.MagnetsElectric; 5 6 import dchip.all; 7 8 import samples.ChipmunkDemo; 9 10 import std.math; 11 import core.stdc.string:strcmp; 12 import core.stdc.stdio:sprintf; 13 14 enum WIDTH = 600; 15 enum HEIGHT = 400; 16 17 enum SINGMAX = 10; // Maximum number of singularities per body 18 enum NMAG = 10; // Number of magnets 19 enum NCHG = 10; // Number of charged bodies 20 enum NMIX = 10; // Number of charged magnets 21 22 enum COU_MKS = 8.987551787e9; // Some physical constants 23 enum MAG_MKS = 1e-7; 24 25 // Prototypes 26 alias void function(DataforForce* data)SingForceFunc; 27 28 // Structures 29 // Singularities 30 struct ActorSingularity{ 31 // Number of singularities 32 int Nsing; 33 // Value of the singularities 34 cpFloat value[SINGMAX]; 35 // Type of the singularities 36 char type[SINGMAX][100]; 37 // Global position of the singularities 38 cpVect Gpos[SINGMAX]; 39 // Local position of the singularities 40 cpVect position[SINGMAX]; 41 // Angle of the singularities measured in the body axes 42 cpFloat angle[SINGMAX]; 43 // Angle of the singularities measured from x 44 cpFloat Gangle[SINGMAX]; 45 // Force function 46 SingForceFunc force_func[SINGMAX]; 47 // Force function 48 SingForceFunc torque_func[SINGMAX]; 49 } 50 alias ActorSingularity Sing; 51 52 // Data for the force functions 53 struct DataforForce{ 54 //Everything in global coordinates 55 // Position of the source 56 cpVect p0; 57 // Observed position 58 cpVect p; 59 // Relative position source-observed 60 cpVect relp; 61 // distance, disntace^2, ditance ^3 62 cpFloat r[3]; 63 // angle of the source 64 cpFloat ang0; 65 // angle of the observed singularity 66 cpFloat ang; 67 // Foce value 68 cpVect F; 69 // Torque value 70 cpFloat T; 71 } 72 alias DataforForce ForceData; 73 74 // Global Varibales 75 static cpSpace *space; 76 77 78 // **** Forces ****** // 79 // Calculate the forces between two bodies. all this functions requieres 80 // a pointer to an structure with the necessary fields. 81 82 // forces between charges 83 static void 84 CoulombForce(ForceData* data){ 85 data.F=cpvmult(cpvnormalize(data.relp),COU_MKS/data.r[1]); 86 } 87 88 // forces between magnets 89 static void 90 MagDipoleForce(ForceData* data){ 91 static cpFloat phi,alpha,beta,Fr,Fphi; 92 93 // Angle of the relative position vector 94 phi=cpvtoangle(data.relp); 95 alpha=data.ang0; 96 beta=data.ang; 97 98 alpha =phi - alpha; 99 beta = phi - beta; 100 101 102 // Components in polar coordinates 103 Fr=(2.0e0*cos(alpha)*cos(beta) - sin(alpha)*sin(beta)); 104 Fphi=sin(alpha+beta); 105 // printf("%g %g %g %g %g\n",phi,alpha,beta,Fphi); 106 107 // Cartesian coordinates 108 data.F=cpv(Fr*cos(phi)-Fphi*sin(phi),Fr*sin(phi)+Fphi*cos(phi)); 109 data.F=cpvmult(data.F,-3.0*MAG_MKS/(data.r[1]*data.r[1])); 110 } 111 112 static void 113 MagDipoleTorque(ForceData* data){ 114 static cpFloat phi,alpha,beta; 115 116 phi=cpvtoangle(data.relp); 117 alpha=data.ang0; 118 beta=data.ang; 119 alpha =phi - alpha; 120 beta = phi - beta; 121 122 // Torque. Though we could use a component of F to save some space, 123 // we use another variables for the sake of clarity. 124 125 data.T=(MAG_MKS/data.r[2])*(3.0e0*cos(alpha)*sin(beta) + sin(alpha-beta)); 126 } 127 // ******* // 128 129 // This function fills the data structure for the force functions 130 // The structure Sing has the information about the singularity (charge or magnet) 131 static void 132 FillForceData(Sing* source,int inds, Sing* obs,int indo, ForceData* data) 133 { 134 // Global Position and orientation of the source singularity 135 data.p0=source.Gpos[inds]; 136 data.ang0=source.Gangle[inds]; 137 138 // Global Position and orientation of the observed singularity 139 data.p=obs.Gpos[indo]; 140 data.ang=obs.Gangle[indo]; 141 142 // Derived magnitudes 143 data.relp=cpvsub(data.p,data.p0); //Relative position 144 data.r[0]=cpvlength(data.relp); // Distance 145 data.r[1]=cpvlengthsq(data.relp); // Square Distance 146 data.r[2]=data.r[0]*data.r[1]; // Cubic distance 147 148 source.force_func[inds](data); // The value of the force 149 data.F= cpvmult(data.F,source.value[inds]*obs.value[indo]); 150 } 151 152 // Calculation of the interaction 153 static void 154 LRangeForceApply(cpBody *a, cpBody *b){ 155 156 Sing* aux = cast(Sing*)a.data; 157 Sing* aux2 = cast(Sing*)b.data; 158 cpVect delta; 159 // General data needed to calculate interaction 160 static ForceData fdata; 161 fdata.F=cpvzero; 162 163 // Calculate the forces between the charges of different bodies 164 for (int i=0; i<aux.Nsing; i++) 165 { 166 for (int j=0; j<aux2.Nsing; j++) 167 { 168 if(!strcmp(aux.type[i].ptr,aux2.type[j].ptr)) 169 { 170 //printf("%s %s\n",aux.type[i],aux2.type[j]); 171 FillForceData (aux2,j,aux,i,&fdata); 172 173 //Force applied to body A 174 delta=cpvsub(aux.Gpos[i], a.p); 175 cpBodyApplyForce(a,fdata.F, delta); 176 177 if(aux.torque_func[i] != null) 178 { 179 //Torque on A 180 aux.torque_func[i](&fdata); 181 a.t += aux.value[i]*aux2.value[j]*fdata.T; 182 183 } 184 } 185 } 186 } 187 } 188 189 // function for the integration of the positions 190 // The following functions are variations to the starndrd integration in Chipmunk 191 // you can go ack to the standard ones by doing the appropiate changes. 192 static void 193 ChargedBodyUpdatePositionVerlet(cpBody *_body, cpFloat dt) 194 { 195 // Long range interaction 196 cpArray *bodies = space.bodies; 197 static cpBody* B; 198 Sing* aux=cast(Sing*)_body.data; 199 Sing* aux2; 200 201 // General data needed to calculate interaction 202 static ForceData fdata; 203 fdata.F=cpvzero; 204 205 for(int i=0; i< bodies.num; i++) 206 { 207 B=cast(cpBody*)bodies.arr[i]; 208 aux2=cast(Sing*)B.data; 209 210 if(B != _body) 211 { 212 // Calculate the forces between the singularities of different bodies 213 LRangeForceApply(_body, B); 214 } 215 } 216 217 cpVect dp = cpvmult(cpvadd(_body.v, _body.v_bias), dt); 218 dp = cpvadd(dp,cpvmult(cpvmult(_body.f, _body.m_inv), 0.5e0*dt*dt)); 219 _body.p = cpvadd(_body.p, dp); 220 221 cpBodySetAngle(_body, cast(float)(_body.a + (_body.w + _body.w_bias)*dt 222 + 0.5*_body.t*_body.i_inv*dt*dt)); 223 224 // Update position of the singularities 225 aux = cast(Sing*)_body.data; 226 for (int i=0; i<aux.Nsing; i++) 227 { 228 aux.Gpos[i]=cpvadd(_body.p,cpvrotate(cpv(aux.position[i].x, 229 aux.position[i].y), _body.rot)); 230 aux.Gangle[i]= aux.angle[i] + _body.a; 231 } 232 233 234 _body.v_bias = cpvzero; 235 _body.w_bias = 0.0f; 236 } 237 238 // function for the integration of the velocities 239 static void 240 ChargedBodyUpdateVelocityVerlet(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt) 241 { 242 _body.v = cpvadd(_body.v, cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt)); 243 _body.w = _body.w + _body.t*_body.i_inv*0.5e0*dt; 244 245 _body.f = cpvzero; 246 _body.t = 0.0e0; 247 248 // Long range interaction 249 cpArray *bodies = space.bodies; 250 static cpBody* B; 251 252 // General data needed to calculate interaction 253 static ForceData fdata; 254 fdata.F=cpvzero; 255 256 for(int i=0; i< bodies.num; i++) 257 { 258 B=cast(cpBody*)bodies.arr[i]; 259 260 if(B != _body) 261 { 262 // Calculate the forces between the singularities of different bodies 263 LRangeForceApply(_body, B); 264 } 265 } 266 _body.v = cpvadd(cpvmult(_body.v,damping), cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt)); 267 _body.w = _body.w*damping + _body.t*_body.i_inv*0.5e0*dt; 268 } 269 270 static void 271 update(int ticks) 272 { 273 enum int steps = 10; 274 enum cpFloat dt = 1.0/60.0/cast(cpFloat)steps; 275 276 cpArray *bodies = space.bodies; 277 278 for(int i=0; i< bodies.num; i++) 279 cpBodyResetForces(cast(cpBody*)bodies.arr[i]); 280 281 for(int i=0; i<steps; i++){ 282 cpSpaceStep(space, dt); 283 } 284 285 } 286 287 static void 288 make_mag(cpVect p, cpFloat ang, cpFloat mag) 289 { 290 int nverts=6; 291 cpVect verts[] = [ 292 cpv(-10,-10), 293 cpv(-10, 10), 294 cpv( 10, 10), 295 cpv( 15, 5), 296 cpv( 15, -5), 297 cpv( 10,-10) 298 ]; 299 300 cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0f, nverts, verts.ptr, cpvzero)); 301 _body.p = p; 302 _body.v = cpvzero; 303 cpBodySetAngle(_body, ang); 304 _body.w = 0.0e0; 305 306 // Load the singularities 307 Sing *magnet=cast(Sing*)cpcalloc(1,Sing.sizeof); 308 magnet.Nsing=1; 309 magnet.value[0]=mag; 310 sprintf(magnet.type[0].ptr,"magdipole"); 311 312 // The position and angle could be different form the one of the body 313 magnet.position[0]=cpvzero; 314 magnet.Gpos[0]=cpvadd(p,magnet.position[0]); 315 magnet.angle[0]=0.0f; 316 magnet.Gangle[0]=ang; 317 318 magnet.force_func[0]=&MagDipoleForce; 319 magnet.torque_func[0]=&MagDipoleTorque; 320 321 _body.data=magnet; 322 323 _body.position_func=&ChargedBodyUpdatePositionVerlet; 324 _body.velocity_func=&ChargedBodyUpdateVelocityVerlet; 325 cpSpaceAddBody(space, _body); 326 327 cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero); 328 shape.e = 0.0; shape.u = 0.7; 329 cpSpaceAddShape(space, shape); 330 } 331 332 static void 333 make_charged(cpVect p, cpFloat chg) 334 { 335 int nverts=4; 336 cpVect verts[] = [ 337 cpv(-10,-10), 338 cpv(-10, 10), 339 cpv( 10, 10), 340 cpv( 10,-10) 341 ]; 342 343 cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero)); 344 _body.p = p; 345 _body.v = cpvzero; 346 cpBodySetAngle(_body, 0.0f); 347 _body.w = 0.0e0; 348 349 // Load the singularities 350 Sing *charge=cast(Sing*)cpcalloc(1,Sing.sizeof); 351 charge.Nsing=1; 352 charge.value[0]=chg; 353 sprintf(charge.type[0].ptr,"electrical\0"); 354 355 // The position and angle could be different form the one of the body 356 charge.position[0]=cpvzero; 357 charge.Gpos[0]=cpvadd(p,charge.position[0]); 358 charge.Gangle[0]=0; 359 charge.angle[0]=0.0f; 360 361 charge.force_func[0]=&CoulombForce; 362 charge.torque_func[0]=null; 363 364 _body.data=charge; 365 366 _body.position_func=&ChargedBodyUpdatePositionVerlet; 367 _body.velocity_func=&ChargedBodyUpdateVelocityVerlet; 368 cpSpaceAddBody(space, _body); 369 370 cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero); 371 shape.e = 0.0; shape.u = 0.7; 372 cpSpaceAddShape(space, shape); 373 } 374 void 375 make_mix(cpVect p, cpFloat ang, cpFloat mag,cpFloat chg) 376 { 377 int nverts=5; 378 cpVect verts[] = [ 379 cpv(-10,-10), 380 cpv(-10, 10), 381 cpv( 10, 10), 382 cpv( 20, 0), 383 cpv( 10,-10) 384 ]; 385 386 cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero)); 387 _body.p = p; 388 _body.v = cpvzero; 389 cpBodySetAngle(_body, ang); 390 _body.w = 0.0e0; 391 392 // Load the singularities 393 Sing *mix=cast(Sing*)cpcalloc(1,Sing.sizeof); 394 mix.Nsing=2; 395 mix.value[0]=mag; 396 mix.value[1]=chg; 397 sprintf(mix.type[0].ptr,"magdipole\0"); 398 sprintf(mix.type[1].ptr,"electrical\0"); 399 400 // The position and angle could be different form the one of the body 401 mix.position[0]=cpvzero; 402 mix.Gpos[0]=cpvadd(p,mix.position[0]); 403 mix.position[1]=cpvzero; 404 mix.Gpos[1]=cpvadd(p,mix.position[1]); 405 mix.Gangle[0]=ang; 406 mix.Gangle[1]=ang; 407 mix.angle[0]=0.0f; 408 mix.angle[1]=0.0f; 409 410 mix.force_func[0]=&MagDipoleForce; 411 mix.force_func[1]=&CoulombForce; 412 mix.torque_func[0]=&MagDipoleTorque; 413 mix.torque_func[1]=null; 414 415 _body.data=mix; 416 417 _body.position_func=&ChargedBodyUpdatePositionVerlet; 418 _body.velocity_func=&ChargedBodyUpdateVelocityVerlet; 419 cpSpaceAddBody(space, _body); 420 421 cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero); 422 shape.e = 0.0; shape.u = 0.7; 423 cpSpaceAddShape(space, shape); 424 } 425 426 427 static cpSpace* 428 init() 429 { 430 cpResetShapeIdCounter(); 431 space = cpSpaceNew(); 432 space.iterations = 5; 433 space.gravity = cpvzero; //cpv(0,-100); 434 435 // Screen border 436 /* shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f); 437 shape.e = 1.0; shape.u = 1.0; 438 cpSpaceAddShape(space, shape); 439 440 shape = cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f); 441 shape.e = 1.0; shape.u = 1.0; 442 cpSpaceAddShape(space, shape); 443 444 shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f); 445 shape.e = 1.0; shape.u = 1.0; 446 cpSpaceAddShape(space, shape); 447 448 // Reference line 449 // Does not collide with other objects, we just want to draw it. 450 shape = cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f); 451 shape.collision_type = 1; 452 cpSpaceAddShape(space, shape); 453 // Add a collision pair function to filter collisions 454 cpSpaceAddCollisionPairFunc(space, 0, 1, null, null); 455 */ 456 457 //srand(cast(uint) time(null)); 458 cpVect p; 459 cpFloat ang; 460 461 // Create magnets 462 for(int i=0; i<NMAG; i++) 463 { 464 p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f; 465 p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f; 466 ang=(2.0e0*frand() - 1.0e0)*3.1415; 467 make_mag(p, ang,1.0e7); 468 } 469 470 // Create charged objects 471 for(int i=0; i<NCHG; i++) 472 { 473 p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f; 474 p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f; 475 ang=(2.0e0*frand() - 1.0e0)*3.1415; 476 make_charged(p,1.0e-3*pow(-1.0,i%2)); 477 } 478 479 // Create charged magnets objects 480 for(int i=0; i<NMIX; i++) 481 { 482 p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f; 483 p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f; 484 ang=(2.0e0*frand() - 1.0e0)*3.1415; 485 make_mix(p, ang,1.0e7*pow(-1.0,i%2), 1.0e-3*pow(-1.0,i%2)); 486 } 487 488 return space; 489 } 490 491 static void 492 destroy() 493 { 494 ChipmunkDemoFreeSpaceChildren(space); 495 cpSpaceFree(space); 496 } 497 498 chipmunkDemo MagnetsElectric = { 499 "Magnets and Electric Charges (By: Juan Pablo Carbajal)", 500 null, 501 &init, 502 &update, 503 &destroy, 504 };