JSDM

HTML

1
 
1
<canvas id="screen"/>

CSS

x
 
1
html, body {
2
  -ms-touch-action:none;
3
  -ms-content-zooming:none;
4
  margin: 0;
5
  padding: 0;
6
  background: #333;
7
  position: absolute;
8
  width: 100%;
9
  height: 100%;
10
  color:#FFF;
11
}
12
13
#screen {
14
  width:100%;
15
  height:100%;
16
  margin:auto auto;
17
  position: fixed;
18
  top: 0; 
19
  bottom: 0;
20
  left: 0; 
21
  right: 0;
22
  background: #000;
23
  cursor: pointer;
24
}
? ?
? ?
必须是有效的URL
+ 添加另一个资源

JS

1306
 
1
/** 
2
* PHY2D physics 2D engine
3
* C# and AS3 code by Paul Firth http://www.wildbunny.co.uk/
4
* JavaScript adaptation by @ge1doot http://codepen.io/ge1doot/
5
*/ 
6
7
 
8
! function ()
9
{
10
  "use strict";
11
  
12
  /** 
13
  * init screen and pointer
14
  */ 
15
16
  var screen = ge1doot.screen.init("screen", function ()
17
  {
18
    PHY2D.zoom = 1;
19
    PHY2D.deleteStatic();
20
    PHY2D.rectangle(screen.width / 2, screen.height + 50, screen.width, 100, 0, 0);
21
    PHY2D.rectangle(screen.width / 2, -3000, screen.width, 100, 0, 0);
22
    PHY2D.rectangle(-50, -1500 + screen.height, 100, 3000 + screen.height, 0, 0);
23
    PHY2D.rectangle(screen.width + 50, -1500 + screen.height, 100, 3000 + screen.height, 0, 0);
24
    PHY2D.zoom = screen.width / 1660;
25
  }, false);
26
  
27
  var ctx = screen.ctx;
28
  
29
  if (!ctx.setLineDash)
30
  {
31
    ctx.setLineDash = function () {}
32
  }
33
  
34
  var pointer = screen.pointer.init(
35
  {
36
    up: function ()
37
    {
38
      PHY2D.stopManipulate();
39
    }
40
  });
41
42
  /** 
43
  * PHY2D engine
44
  */ 
45
    
46
  var PHY2D = {
47
    kGravity: 5,
48
    kTimeStep: 1 / 60,
49
    kFriction: 0.5,
50
    kMotionAABB: true,
51
    kImgPath: "http://www.dhteumeuleu.com/images/"
52
  }
53
  
54
  /** 
55
  * Vector 2D library
56
  */ 
57
  
58
  PHY2D.Vector = function (x, y)
59
  {
60
    this.x = x || 0.0;
61
    this.y = y || 0.0;
62
  }
63
  
64
  PHY2D.Vector.prototype = {
65
  
66
  
67
    set: function (x, y)
68
    {
69
      this.x = x;
70
      this.y = y;
71
      return this;
72
    },
73
    
74
    
75
    dot: function (v)
76
    {
77
      return this.x * v.x + this.y * v.y;
78
    },
79
    
80
    
81
    lenSqr: function ()
82
    {
83
      return this.x * this.x + this.y * this.y;
84
    },
85
    
86
    
87
    len: function ()
88
    {
89
      return Math.sqrt(this.x * this.x + this.y * this.y);
90
    },
91
    
92
    
93
    dist: function (v)
94
    {
95
      var dx = v.x - this.x;
96
      var dy = v.y - this.y;
97
      return Math.sqrt(dx * dx + dy * dy);
98
    },
99
    
100
    
101
    transform: function (v, m)
102
    {
103
      this.x = m.cos * v.x - m.sin * v.y + m.tx;
104
      this.y = m.sin * v.x + m.cos * v.y + m.ty;
105
      return this;
106
    },
107
    
108
    
109
    rotate: function (v, m)
110
    {
111
      this.x = m.cos * v.x - m.sin * v.y;
112
      this.y = m.sin * v.x + m.cos * v.y;
113
      return this;
114
    },
115
    
116
    
117
    normal: function (a, b)
118
    {
119
      var x = a.x - b.x,
120
        y = a.y - b.y,
121
        len = Math.sqrt(x * x + y * y);
122
      this.x = -y / len;
123
      this.y =  x / len;
124
      return this;
125
    },
126
    
127
    
128
    project: function (a, b, n)
129
    {
130
      var x = a.x - b.x,
131
        y = a.y - b.y,
132
        len = Math.sqrt(x * x + y * y);
133
      return (-y / len) * n.x + (x / len) * n.y;
134
    },
135
    
136
    
137
    addScale: function (v1, v2, s)
138
    {
139
      this.x = v1.x + (v2.x * s);
140
      this.y = v1.y + (v2.y * s);
141
      return this;
142
    },
143
    
144
    
145
    subScale: function (v1, v2, s)
146
    {
147
      this.x = v1.x - (v2.x * s);
148
      this.y = v1.y - (v2.y * s);
149
      return this;
150
    },
151
    
152
    
153
    add: function (v1, v2)
154
    {
155
      this.x = v1.x + v2.x;
156
      this.y = v1.y + v2.y;
157
      return this;
158
    },
159
    
160
    
161
    sub: function (v1, v2)
162
    {
163
      this.x = v1.x - v2.x;
164
      this.y = v1.y - v2.y;
165
      return this;
166
    },
167
    
168
    
169
    scale: function (v1, s)
170
    {
171
      this.x = v1.x * s;
172
      this.y = v1.y * s;
173
      return this;
174
    },
175
    
176
    
177
    perpSelf: function ()
178
    {
179
      var x = this.x;
180
      this.x = -this.y;
181
      this.y = x;
182
      return this;
183
    },
184
    
185
    
186
    perp: function (v1)
187
    {
188
      this.x = -v1.y;
189
      this.y =  v1.x;
190
      return this;
191
    },
192
    
193
    
194
    inv: function (v1)
195
    {
196
      this.x = -v1.x;
197
      this.y = -v1.y;
198
      return this;
199
    },
200
    
201
    
202
    copy: function (v1)
203
    {
204
      this.x = v1.x;
205
      this.y = v1.y;
206
      return this;
207
    },
208
    
209
    
210
    unit: function ()
211
    {
212
      var invLen = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y);
213
      this.x *= invLen;
214
      this.y *= invLen;
215
      return this;
216
    },
217
    
218
    
219
    unitLen: function (len)
220
    {
221
      var invLen = 1.0 / len;
222
      this.x *= invLen;
223
      this.y *= invLen;
224
      return this;
225
    },
226
    
227
    
228
    clamp: function (v, min, max)
229
    {
230
      if (v > max) v = max;
231
      else if (v < min) v = min;
232
      return v;
233
    },
234
    
235
    
236
    rotateIntoSpaceOf: function (a, m)
237
    {
238
      var dx = -a.x,
239
        dy = -a.y;
240
      this.x = dx *  m.cos + dy * m.sin;
241
      this.y = dx * -m.sin + dy * m.cos;
242
      return this;
243
    },
244
    
245
    
246
    projectPointOntoEdge: function (p, e0, e1)
247
    {
248
      var ex = e1.x - e0.x;
249
      var ey = e1.y - e0.y;
250
      var t = (ex * (p.x - e0.x) + ey * (p.y - e0.y)) / (ex * ex + ey * ey);
251
      if (t < 0) t = 0; else if (t > 1) t = 1;
252
      this.x = e0.x + (ex * t);
253
      this.y = e0.y + (ey * t);
254
      return this;
255
    },
256
    
257
    
258
    /** 
259
    * pseudo SIMD array vectors
260
    */ 
261
    
262
    array: function (n, values)
263
    {
264
      var array = new Array(n);
265
      array.min = new PHY2D.Vector();
266
      array.max = new PHY2D.Vector();
267
      for (var i = 0; i < n; i++)
268
      {
269
        array[i] = new PHY2D.Vector(
270
          values ? values[i * 2 + 0] : 0.0,
271
          values ? values[i * 2 + 1] : 0.0
272
        );
273
      }
274
      
275
      
276
      array.transform = function (v, m)
277
      {
278
        for (var i = 0, len = this.length; i < len; i++)
279
        {
280
          var vi = v[i],
281
            elem = this[i];
282
          var x = m.cos * vi.x - m.sin * vi.y + m.tx;
283
          var y = m.sin * vi.x + m.cos * vi.y + m.ty;
284
          if (x < this.min.x) this.min.x = x;
285
          if (y < this.min.y) this.min.y = y;
286
          if (x > this.max.x) this.max.x = x;
287
          if (y > this.max.y) this.max.y = y;
288
          elem.x = x;
289
          elem.y = y;
290
        }
291
        return this;
292
      }
293
      
294
      
295
      array.rotate = function (v, m)
296
      {
297
        for (var i = 0, len = this.length; i < len; i++)
298
        {
299
          var vi = v[i],
300
            elem = this[i];
301
          elem.x = m.cos * vi.x - m.sin * vi.y;
302
          elem.y = m.sin * vi.x + m.cos * vi.y;
303
        }
304
        return this;
305
      }
306
      
307
      
308
      array.resetMinmax = function ()
309
      {
310
        this.min.x = 100000.0;
311
        this.min.y = 100000.0;
312
        this.max.x = -100000.0;
313
        this.max.y = -100000.0;
314
      }
315
      
316
      
317
      array.normal = function (points)
318
      {
319
        for (var i = 0; i < this.length; i++)
320
        {
321
          this[i].normal(
322
            points[(i + 1) % this.length],
323
            points[i]
324
          );
325
        }
326
        return this;
327
      }
328
      
329
      
330
      return array;
331
    }
332
  }
333
  
334
335
  /** 
336
  * Matrix container
337
  */ 
338
  
339
  PHY2D.Matrix = function ()
340
  {
341
    this.cos = 0.0;
342
    this.sin = 0.0;
343
    this.tx  = 0.0;
344
    this.ty  = 0.0;
345
  }
346
  
347
  
348
  PHY2D.Matrix.prototype.set = function (x, y, a)
349
  {
350
    this.cos = Math.cos(a);
351
    this.sin = Math.sin(a);
352
    this.tx  = x;
353
    this.ty  = y;
354
    return this;
355
  }
356
  
357
  /** 
358
  * temporary working vectors
359
  */ 
360
  
361
  PHY2D.v0 = new PHY2D.Vector();
362
  PHY2D.v1 = new PHY2D.Vector();
363
  PHY2D.v2 = new PHY2D.Vector();
364
  PHY2D.v3 = new PHY2D.Vector();
365
  PHY2D.v4 = new PHY2D.Vector();
366
  PHY2D.v5 = new PHY2D.Vector();
367
  PHY2D.z0 = new PHY2D.Vector();
368
  
369
  /** 
370
  * objects and contacts list arrays
371
  */ 
372
  
373
  PHY2D.objects = [];
374
  PHY2D.manipulate = null;
375
  PHY2D.joints = [];
376
  PHY2D.contacts = [];
377
  PHY2D.contacts.index = 0;
378
  PHY2D.contacts.create = function (A, B, pa, pb, dist, normal)
379
  {
380
    if (!this[this.index]) this[this.index] = new PHY2D.Contact();
381
    this[this.index++].set(A, B, pa, pb, dist, normal);
382
  }
383
  
384
  
385
  /** 
386
  * AABB container
387
  */ 
388
389
  
390
  PHY2D.AABB = function ()
391
  {
392
    this.x = 0.0;
393
    this.y = 0.0;
394
    this.w = 0.0;
395
    this.h = 0.0;
396
  }
397
  
398
  /** 
399
  * Rigidbody base PROTOTYPE definition
400
  */ 
401
  
402
  PHY2D.RigidBody = {};
403
  
404
  
405
  /** 
406
  * Init new Rigidbody object
407
  */ 
408
  
409
  PHY2D.RigidBody.initBase = function (x, y, w, h, mass, angle, img)
410
  {
411
    this.img = null;
412
    if (img)
413
    {
414
      var image = new Image();
415
      image.src = PHY2D.kImgPath + img.src;
416
      this.img = {
417
        w: (img.w ? img.w * PHY2D.zoom : w),
418
        h: (img.h ? img.h * PHY2D.zoom : h),
419
        x: (img.x ? img.x * PHY2D.zoom : 0),
420
        y: (img.y ? img.y * PHY2D.zoom : 0),
421
        elem: image
422
      }
423
    }
424
    this.pos       = new PHY2D.Vector(x, y);
425
    this.vel       = new PHY2D.Vector();
426
    this.ang       = angle;
427
    this.angVel    = 0.0;
428
    this.invMass   = mass ? 1 / mass : 0;
429
    this.matrix    = new PHY2D.Matrix().set(x, y, angle);
430
    this.nextFrame = new PHY2D.Matrix();
431
    this.aabb      = new PHY2D.AABB();
432
    this.c1        = new PHY2D.Vector();
433
    this.c0        = new PHY2D.Vector();
434
    this.normal    = new PHY2D.Vector();
435
    this.static    = false;
436
  }
437
  
438
  /** 
439
  * draw rigid body image
440
  */ 
441
  
442
  PHY2D.RigidBody.draw = function ()
443
  {
444
    var img = this.img;
445
    if (img)
446
    {
447
      ctx.save();
448
      ctx.translate(this.pos.x, this.pos.y);
449
      ctx.rotate(this.ang);
450
      ctx.drawImage(img.elem, -img.w * 0.5 + img.x, -img.h * 0.5 + img.y, img.w, img.h);
451
      ctx.restore();
452
    }
453
  }
454
  
455
  /** 
456
  * Rigidbody integration
457
  */ 
458
459
  PHY2D.RigidBody.integrate = function ()
460
  {
461
    // gravity
462
    if (this.invMass > 0) this.vel.y += PHY2D.kGravity;
463
    // update position
464
    var vx = this.vel.x * PHY2D.kTimeStep;
465
    var vy = this.vel.y * PHY2D.kTimeStep;
466
    var va = this.angVel * PHY2D.kTimeStep;
467
    this.pos.x += vx;
468
    this.pos.y += vy;
469
    this.ang   += va;
470
    // update matrix
471
    this.matrix.set(this.pos.x, this.pos.y, this.ang);
472
    if (PHY2D.kMotionAABB)
473
    {
474
      this.nextFrame.set(this.pos.x + vx, this.pos.y + vy, this.ang + va);
475
    }
476
    // compute motion AABB
477
    if (!this.static) this.motionAABB();
478
    else
479
    {
480
      if (this.invMass === 0)
481
      {
482
        this.static = true;
483
        this.motionAABB();
484
      }
485
    }
486
    // manipulate
487
    if (pointer.active && !PHY2D.manipulate && this.invMass)
488
    {
489
      if (this.isPointInPoly(pointer.pos.x, pointer.pos.y))
490
      {
491
        PHY2D.manipulate = new PHY2D.Manipulate(this);
492
        PHY2D.manipulate.solve();
493
      }
494
    }
495
  } 
496
  
497
498
  /** 
499
  * Polygon PROTOTYPE definition
500
  * extends RigidBody base prototype
501
  */ 
502
  
503
  PHY2D.Polygon = Object.create(PHY2D.RigidBody);
504
  
505
  /** 
506
  * Init new Polygon object
507
  */ 
508
  
509
  PHY2D.Polygon.init = function (x, y, w, h, vertices, mass, angle, img)
510
  {
511
    this.type = "poly";
512
    this.initBase(x, y, w, h, mass, angle, img);
513
    this.length = (vertices.length / 2) | 0;
514
    // vertices
515
    this.localSpacePoints  = new PHY2D.Vector().array(this.length, vertices);
516
    this.localSpaceNormals = new PHY2D.Vector().array(this.length).normal(this.localSpacePoints);
517
    this.worldSpaceNormals = new PHY2D.Vector().array(this.length);
518
    this.worldSpacePoints  = new PHY2D.Vector().array(this.length);
519
    // calculate inverse inertia tensor
520
    this.invI = (this.invMass > 0) ? 1 / ((1 / this.invMass) * (w * w + h * h) / 3) : 0
521
    return this;
522
  }
523
524
  /** 
525
  * AABB motion
526
  */ 
527
  
528
  PHY2D.Polygon.motionAABB = function ()
529
  {
530
    this.worldSpacePoints.resetMinmax();
531
    PHY2D.kMotionAABB && this.worldSpacePoints.transform(this.localSpacePoints, this.nextFrame);
532
    this.worldSpacePoints.transform(this.localSpacePoints, this.matrix);
533
    this.worldSpaceNormals.rotate(this.localSpaceNormals, this.matrix);
534
    var min = this.worldSpacePoints.min;
535
    var max = this.worldSpacePoints.max;
536
    this.aabb.x = (min.x + max.x) * 0.5;
537
    this.aabb.y = (min.y + max.y) * 0.5;
538
    this.aabb.w = (max.x - min.x) * 0.5;
539
    this.aabb.h = (max.y - min.y) * 0.5;
540
  }
541
  
542
  /** 
543
  * is point in polygon ?
544
  */ 
545
  
546
  PHY2D.Polygon.isPointInPoly = function (x, y)
547
  {
548
    var c = false;
549
    for (var p = this.worldSpacePoints, i = -1, l = this.length, j = l - 1; ++i < l; j = i)
550
    {
551
      ((p[i].y <= y && y < p[j].y) || (p[j].y <= y && y < p[i].y))
552
      && (x <= (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)
553
      && (c = !c);
554
    }
555
    return c;
556
  }
557
558
  /** 
559
  * Generate contact points
560
  */ 
561
  
562
  PHY2D.Polygon.generateContacts = function (B)
563
  {
564
    if (B.type === "poly")   return this.polyVsPoly(B);
565
    if (B.type === "circle") return this.polyVsCircle(B);
566
  }
567
  
568
  /** 
569
  * Polygon vs Polygon collision
570
  * http://www.wildbunny.co.uk/blog/2011/04/20/collision-detection-for-dummies/
571
  * Minkowski Difference
572
  */ 
573
574
  PHY2D.Polygon.polyVsPoly = function (that)
575
  {
576
    var face, vertex, vertexRect, faceRect, fp, va, vb, vc, wsN, wdV0, wdV1, wsV0, wsV1;
577
    var mp = PHY2D.mostPenetrating, ms = PHY2D.mostSeparated;
578
    ms.set(100000, -1, -1, 0);
579
    mp.set(-100000, -1, -1, 0);
580
    // face of A, vertices of B
581
    this.featurePairJudgement(that, 2);
582
    // faces of B, vertices of A
583
    that.featurePairJudgement(this, 1);
584
    if (ms.dist > 0 && ms.fpc !== 0)
585
    {
586
      // objects are separated
587
      face = ms.edge;
588
      vertex = ms.closestI;
589
      fp = ms.fpc;
590
    }
591
    else if (mp.dist <= 0)
592
    {
593
      // objects are penetrating
594
      face = mp.edge;
595
      vertex = mp.closestI;
596
      fp = mp.fpc;
597
    }
598
    if (fp === 1)
599
    {
600
      vertexRect = this;
601
      faceRect = that;
602
    }
603
    else
604
    {
605
      vertexRect = that;
606
      faceRect = this;
607
    }
608
    wsN = faceRect.worldSpaceNormals[face];
609
    va = vertexRect.worldSpacePoints[(vertex - 1 + vertexRect.length) % vertexRect.length];
610
    vb = vertexRect.worldSpacePoints[vertex];
611
    vc = vertexRect.worldSpacePoints[(vertex + 1) % vertexRect.length];
612
    if (PHY2D.v0.project(vb, va, wsN) < PHY2D.v1.project(vc, vb, wsN))
613
    {
614
      wdV0 = va;
615
      wdV1 = vb;
616
    }
617
    else
618
    {
619
      wdV0 = vb;
620
      wdV1 = vc;
621
    }
622
    // world space edge
623
    wsV0 = faceRect.worldSpacePoints[face];
624
    wsV1 = faceRect.worldSpacePoints[(face + 1) % faceRect.length];
625
    // form contact
626
    if (fp === 1)
627
    {
628
      this.c0.projectPointOntoEdge(wsV0, wdV0, wdV1);
629
      this.c1.projectPointOntoEdge(wsV1, wdV0, wdV1);
630
      that.c0.projectPointOntoEdge(wdV1, wsV0, wsV1);
631
      that.c1.projectPointOntoEdge(wdV0, wsV0, wsV1);       
632
      this.normal.inv(wsN);
633
    }
634
    else
635
    {
636
      this.c0.projectPointOntoEdge(wdV1, wsV0, wsV1);
637
      this.c1.projectPointOntoEdge(wdV0, wsV0, wsV1);
638
      that.c0.projectPointOntoEdge(wsV0, wdV0, wdV1);
639
      that.c1.projectPointOntoEdge(wsV1, wdV0, wdV1);
640
      this.normal.copy(wsN);
641
    }
642
    var d0 = PHY2D.v1.sub(that.c0, this.c0).dot(this.normal);
643
    var d1 = PHY2D.v1.sub(that.c1, this.c1).dot(this.normal);
644
    // always return closest contact first
645
    if ( d0 < d1 )
646
    {
647
      PHY2D.contacts.create(this, that, this.c0, that.c0, d0, this.normal);
648
      PHY2D.contacts.create(this, that, this.c1, that.c1, d1, this.normal);
649
    }
650
    else
651
    {
652
      PHY2D.contacts.create(this, that, this.c1, that.c1, d1, this.normal);
653
      PHY2D.contacts.create(this, that, this.c0, that.c0, d0, this.normal);
654
    }
655
  }
656
  
657
  /** 
658
  * Polygon vs circle
659
  */ 
660
  
661
  PHY2D.Polygon.polyVsCircle = function (that)
662
  {
663
    var mp = PHY2D.mostPenetrating, ms = PHY2D.mostSeparated;
664
    var v1 = PHY2D.v1;
665
    ms.set(100000, -1, -1, 0);
666
    mp.set(-100000, -1, -1, 0);
667
    var wsN = this.worldSpaceNormals;
668
    var wsP = this.worldSpacePoints;
669
    // track distance from circle centre to polygon
670
    for (var i = 0; i < this.length; i++)
671
    {
672
      // world space edge
673
      var wsV0 = wsP[i];
674
      var wsV1 = wsP[(i + 1) % this.length];
675
      // project onto poly edge and get distance
676
      var dist = v1.projectPointOntoEdge(that.pos, wsV0, wsV1).dist(that.pos);
677
      // track negative
678
      if (dist > mp.dist)
679
      {
680
        mp.dist = dist;
681
        mp.face = i;
682
        this.c0.copy(v1);
683
      }
684
      // track positive
685
      if (dist > 0 && dist < ms.dist)
686
      {
687
        ms.dist = dist;
688
        ms.face = i;
689
        this.c1.copy(v1);
690
      }
691
    }
692
    // account for circle radius - the above check was for sphere centre
693
    var penetration = mp.dist - that.radius;
694
    if (penetration <= 0)
695
    {
696
      // penetration
697
      if (mp.dist <= 0)
698
      {
699
        // normal is always a face normal
700
        this.normal.copy(this.worldSpaceNormals[mp.face]);
701
      }
702
      else
703
      {
704
        // normal can still be vertex normal
705
        this.normal.sub(that.pos, this.c0).unit();
706
      }
707
    }
708
    else
709
    {
710
      // separation
711
      this.normal.sub(that.pos, this.c1).unit();
712
      // normal can also be vertex normal
713
      penetration = ms.dist - that.radius;
714
      this.c0.copy(this.c1);
715
      
716
    }
717
    this.c1.subScale(that.pos, this.normal, that.radius);
718
    PHY2D.contacts.create(this, that, this.c0, this.c1, penetration, this.normal);
719
  }
720
  
721
  /** 
722
  * Feature Pair Judgement
723
  */ 
724
725
  PHY2D.Polygon.featurePairJudgement = function (that, fpc)
726
  {
727
    var wsN, closestI, closest, dist;
728
    var mp = PHY2D.mostPenetrating, ms = PHY2D.mostSeparated;
729
    for (var edge = 0; edge < this.length; edge++)
730
    {
731
      // get support vertices
732
      wsN = this.worldSpaceNormals[edge];
733
      // rotate into RigidBody space
734
      PHY2D.v5.rotateIntoSpaceOf(wsN, that.matrix);
735
      var closestI = -1,
736
        closestD = -100000;
737
      // Get the vertex most in the direction of the given vector
738
      for (var i = 0; i < that.length; i++)
739
      {
740
        var d = PHY2D.v5.dot(that.localSpacePoints[i]);
741
        if (d > closestD)
742
        {
743
          closestD = d;
744
          closestI = i;
745
        }
746
      }
747
      var closest = that.worldSpacePoints[closestI];
748
      PHY2D.v0.sub(closest, this.worldSpacePoints[edge]);
749
      // distance from origin to face 
750
      var dist = PHY2D.v0.dot(wsN);
751
      if (dist > 0)
752
      {
753
        // recompute distance to clamped edge
754
        PHY2D.v1.sub(closest, this.worldSpacePoints[(edge + 1) % this.length]);
755
        // project onto minkowski edge
756
        dist = this.c0.projectPointOntoEdge(PHY2D.z0, PHY2D.v0, PHY2D.v1).lenSqr();
757
        // track separation
758
        if (dist < ms.dist)
759
        {
760
          ms.set(dist, closestI, edge, fpc);
761
        }
762
      }
763
      else
764
      {
765
        // track penetration
766
        if (dist > mp.dist)
767
        {
768
          mp.set(dist, closestI, edge, fpc);
769
        }
770
      }
771
    }
772
  }
773
  
774
  /** 
775
  * Circle PROTOTYPE definition
776
  * extends RigidBody base prototype
777
  */ 
778
  
779
  PHY2D.Circle = Object.create(PHY2D.RigidBody);
780
  
781
  /** 
782
  * Init new circle
783
  */ 
784
  
785
  PHY2D.Circle.init = function (x, y, w, mass, img)
786
  {
787
    this.type = "circle";
788
    this.initBase(x, y, w, w, mass, 0, img);
789
    this.radius = w / 2;
790
    this.invI = (1 / 10) * mass * this.radius * this.radius;
791
    return this;
792
  }
793
  
794
  /** 
795
  * Is point in circle ?
796
  */ 
797
  
798
  PHY2D.Circle.isPointInPoly = function (x, y)
799
  {
800
    var dx = (x - this.pos.x);
801
    var dy = (y - this.pos.y);
802
    if (dx * dx + dy * dy < this.radius * this.radius) return true;
803
    return false;
804
  }
805
  
806
  /** 
807
  * AABB motion -- circle version
808
  */ 
809
  
810
  PHY2D.Circle.motionAABB = function ()
811
  {
812
    var x = this.pos.x + (this.vel.x * PHY2D.kTimeStep);
813
    var y = this.pos.y + (this.vel.y * PHY2D.kTimeStep);
814
    this.aabb.x = (this.pos.x + x) * 0.5;
815
    this.aabb.y = (this.pos.y + y) * 0.5;
816
    this.aabb.w = this.radius + Math.abs(this.pos.x - x) * 0.5;
817
    this.aabb.h = this.radius + Math.abs(this.pos.y - y) * 0.5;
818
  }
819
  
820
  /** 
821
  * generate contact point (circle vs circle)
822
  */ 
823
  
824
  PHY2D.Circle.generateContacts = function (that)
825
  {
826
    if (that.type === "circle")
827
    {
828
      this.normal.sub(that.pos, this.pos);
829
      var distCentre = this.normal.len();
830
      var penetration = distCentre - (this.radius + that.radius);
831
      if ( distCentre === 0 )
832
      {
833
        // default for circles exactly on top of each other
834
        this.normal.set(1, 0);
835
        penetration = -(this.radius + that.radius);
836
      }
837
      else
838
      {
839
        this.normal.unitLen(distCentre);
840
      }
841
      this.c0.addScale(this.pos, this.normal, this.radius);
842
      this.c1.subScale(that.pos, this.normal, that.radius);
843
      // generate point on edge
844
      PHY2D.contacts.create(this, that, this.c0, this.c1, penetration, this.normal);
845
    }
846
    else
847
    {
848
      // jump to poly vs circle
849
      that.polyVsCircle(this);
850
    }
851
  }
852
  
853
  /** 
854
  * Feature Pair Container
855
  */ 
856
857
  PHY2D.FeaturePair = function ()
858
  {
859
    this.dist     = 0;
860
    this.closestI = 0;
861
    this.edge     = 0;
862
    this.fpc      = 0;
863
  }
864
  
865
  PHY2D.FeaturePair.prototype.set = function (dist, closestI, edge, fpc)
866
  {
867
    this.dist = dist;
868
    this.closestI = closestI;
869
    this.edge = edge;
870
    this.fpc = fpc;
871
  }
872
  
873
  PHY2D.mostSeparated   = new PHY2D.FeaturePair();
874
  PHY2D.mostPenetrating = new PHY2D.FeaturePair();
875
876
  /** 
877
  * Contacts Constructor
878
  */ 
879
880
  PHY2D.Contact = function ()
881
  {
882
    this.a           = null;
883
    this.b           = null;
884
    this.normal      = new PHY2D.Vector();
885
    this.tangent     = new PHY2D.Vector();
886
    this.ra          = new PHY2D.Vector();
887
    this.rb          = new PHY2D.Vector();
888
    this.impulse     = new PHY2D.Vector();
889
    this.dv          = new PHY2D.Vector();
890
    this.v1          = new PHY2D.Vector();
891
    this.v2          = new PHY2D.Vector();
892
    this.dist        = 0;
893
    this.impulseN    = 0;
894
    this.impulseT    = 0;
895
    this.invDenom    = 0;
896
    this.invDenomTan = 0;
897
  }
898
  
899
  
900
  PHY2D.Contact.prototype = {
901
    
902
    /** 
903
    * set existing contact object
904
    */ 
905
    
906
    set: function (A, B, pa, pb, dist, normal)
907
    {
908
      var ran, rbn;
909
      this.a = A;
910
      this.b = B;
911
      this.normal.copy(normal);
912
      this.tangent.perp(normal);
913
      this.dist = dist;
914
      this.impulseN = 0;
915
      this.impulseT = 0;
916
      // calculate radius arms
917
      this.ra.sub(pa, A.pos).perpSelf();
918
      this.rb.sub(pb, B.pos).perpSelf();
919
      // compute denominator in impulse equation
920
      ran = this.ra.dot(this.normal);
921
      rbn = this.rb.dot(this.normal);
922
      this.invDenom = 1 / (A.invMass + B.invMass + (ran * ran * A.invI) + (rbn * rbn * B.invI));
923
      ran = this.ra.dot(this.tangent);
924
      rbn = this.rb.dot(this.tangent);
925
      this.invDenomTan = 1 / (A.invMass + B.invMass + (ran * ran * A.invI) + (rbn * rbn * B.invI));
926
    },
927
928
    /** 
929
    * Apply impulse
930
    */ 
931
    
932
    applyImpulse: function ()
933
    {
934
      // linear
935
      this.a.vel.addScale(this.a.vel, this.impulse, this.a.invMass);
936
      this.b.vel.subScale(this.b.vel, this.impulse, this.b.invMass);
937
      // angular
938
      this.a.angVel += this.impulse.dot(this.ra) * this.a.invI;
939
      this.b.angVel -= this.impulse.dot(this.rb) * this.b.invI;
940
    },
941
942
    /** 
943
    * Solve contact
944
    */ 
945
946
    solve: function ()
947
    {
948
      var newImpulse, absMag;
949
      // get all of relative normal velocity
950
      this.dv.sub(
951
        this.v1.addScale(this.b.vel, this.rb, this.b.angVel),
952
        this.v2.addScale(this.a.vel, this.ra, this.a.angVel)
953
      );
954
      // accumulated impulse
955
      newImpulse = (this.dv.dot(this.normal) + this.dist / PHY2D.kTimeStep) * this.invDenom + this.impulseN;
956
      // push only
957
      if (newImpulse > 0) newImpulse = 0;
958
      // apply impulse
959
      this.impulse.scale(this.normal, newImpulse - this.impulseN)
960
      this.applyImpulse();
961
      this.impulseN = newImpulse;
962
      // friction
963
      if (this.dist <= 0)
964
      {
965
        absMag = -this.impulseN * PHY2D.kFriction;
966
        newImpulse = this.impulse.clamp(this.dv.dot(this.tangent) * this.invDenomTan + this.impulseT, -absMag, absMag);
967
        this.impulse.scale(this.tangent, newImpulse - this.impulseT);
968
        this.applyImpulse();
969
        this.impulseT = newImpulse;
970
      }
971
    }
972
  }
973
  
974
  /** 
975
  * constraint (joint) constructor
976
  */ 
977
  
978
  PHY2D.Constraint = function (A, va, B, vb)
979
  {
980
    this.a        = A;
981
    this.b        = B;
982
    this.ra       = new PHY2D.Vector();
983
    this.rb       = new PHY2D.Vector();
984
    this.axis     = new PHY2D.Vector();
985
    this.normal   = new PHY2D.Vector();
986
    this.impulse  = new PHY2D.Vector();
987
    this.dv       = new PHY2D.Vector();
988
    this.va       = new PHY2D.Vector(va[0] * PHY2D.zoom, va[1] * PHY2D.zoom);
989
    this.vb       = new PHY2D.Vector(vb[0] * PHY2D.zoom, vb[1] * PHY2D.zoom);
990
    this.pa       = new PHY2D.Vector();
991
    this.pb       = new PHY2D.Vector();
992
    this.invDenom = 0;
993
    this.impulseN = 0;
994
    return this;
995
  }
996
  
997
  /** 
998
  * constraint pre-solve
999
  */ 
1000
  
1001
  PHY2D.Constraint.prototype.presolve = function ()
1002
  {
1003
    // transform constraint points
1004
    this.pa.transform(this.va, this.a.matrix);
1005
    this.pb.transform(this.vb, this.b.matrix);
1006
    // projection axis
1007
    this.axis.sub(this.pb, this.pa);
1008
    this.normal.scale(this.axis, 1 / (this.axis.len() + 0.0001));
1009
    // calculate radius arms
1010
    this.ra.sub(this.pa, this.a.pos).perpSelf();
1011
    this.rb.sub(this.pb, this.b.pos).perpSelf();
1012
    // compute denominator in impulse equation
1013
    var ran = this.ra.dot(this.normal), rbn = this.rb.dot(this.normal);
1014
    this.invDenom = 1 / (this.a.invMass + this.b.invMass + (ran * ran * this.a.invI) + (rbn * rbn * this.b.invI));
1015
  }
1016
  
1017
  /** 
1018
  * constraint solve
1019
  */ 
1020
  
1021
  PHY2D.Constraint.prototype.solve = function ()
1022
  {
1023
    // calculate relative velocity in the axis, we want to remove this
1024
    this.dv.sub(
1025
      PHY2D.v1.addScale(this.b.vel, this.rb, this.b.angVel),
1026
      PHY2D.v2.addScale(this.a.vel, this.ra, this.a.angVel)
1027
    );
1028
    // accumulated impulse
1029
    var dist = this.axis.dot(this.normal);
1030
    var newImpulse = (this.dv.dot(this.normal) + dist / PHY2D.kTimeStep) * this.invDenom + this.impulseN;
1031
    // apply impulse
1032
    this.impulse.scale(this.normal, newImpulse - this.impulseN)
1033
    this.applyImpulse();
1034
    this.impulseN = newImpulse;
1035
  }
1036
  
1037
  /** 
1038
  * Apply Impulse
1039
  */ 
1040
  
1041
  PHY2D.Constraint.prototype.applyImpulse = PHY2D.Contact.prototype.applyImpulse;
1042
  
1043
  /** 
1044
  * Manipulate Constructor
1045
  */ 
1046
    
1047
  PHY2D.Manipulate = function (B)
1048
  {
1049
    this.b        = B;
1050
    this.rb       = new PHY2D.Vector();
1051
    this.axis     = new PHY2D.Vector();
1052
    this.normal   = new PHY2D.Vector();
1053
    this.impulse  = new PHY2D.Vector();
1054
    this.dv       = new PHY2D.Vector();
1055
    this.vb = (B.type === "circle") ? new PHY2D.Vector() : new PHY2D.Vector().rotateIntoSpaceOf(PHY2D.v1.sub(B.pos, pointer.pos), B.matrix);
1056
    this.pb       = new PHY2D.Vector();
1057
    return this;
1058
  }
1059
  
1060
  /** 
1061
  * solve Manipulate Constraint
1062
  */ 
1063
  
1064
  PHY2D.Manipulate.prototype.solve = function ()
1065
  {
1066
    this.pb.transform(this.vb, this.b.matrix);
1067
    this.axis.sub(this.pb, pointer.pos);
1068
    this.normal.scale(this.axis, 1 / (this.axis.len() + 0.0001));
1069
    this.rb.sub(this.pb, this.b.pos).perpSelf();
1070
    this.dv.addScale(this.b.vel, this.rb, this.b.angVel);
1071
    this.impulse.scale(this.normal, this.dv.dot(this.normal) + this.axis.dot(this.normal) * 2);
1072
    this.b.vel.sub(this.b.vel, this.impulse);
1073
    this.b.angVel -= this.impulse.dot(this.rb) * this.b.invI;
1074
  }
1075
  
1076
  /** 
1077
  * Rendering
1078
  */ 
1079
  
1080
  PHY2D.render = function ()
1081
  {
1082
1083
    /** 
1084
    * brute force AABB broad phase
1085
    * SAP, grids, quadtrees don't do any better < 100 objects
1086
    */ 
1087
  
1088
    PHY2D.contacts.index = 0;
1089
    for (var i = 0, len = PHY2D.objects.length; i < len - 1; i++)
1090
    {
1091
      var A = PHY2D.objects[i];
1092
      for (var j = i + 1; j < len; j++)
1093
      {
1094
        var B = PHY2D.objects[j];
1095
        if (A.invMass || B.invMass)
1096
        {
1097
          var a = A.aabb,
1098
            b = B.aabb;
1099
          if (
1100
            Math.abs(b.x - a.x) - (a.w + b.w) < 0 &&
1101
            Math.abs(b.y - a.y) - (a.h + b.h) < 0
1102
          ) {
1103
          
1104
            /** 
1105
            * generate speculative contact
1106
            */ 
1107
            
1108
            A.generateContacts(B);
1109
            
1110
          }
1111
        }
1112
      }
1113
    }
1114
    
1115
    var contacts = PHY2D.contacts, len = contacts.index;
1116
    var joints = PHY2D.joints, cln = joints.length;
1117
1118
    /** 
1119
    * pre-solve constraints
1120
    */ 
1121
    
1122
    for (var i = 0; i < cln; i++)
1123
    {
1124
      PHY2D.joints[i].presolve();
1125
    }
1126
    
1127
    
1128
    for (var j = 0; j < 5; j++) // numIterations
1129
    {
1130
      
1131
      /** 
1132
      * solve constraints
1133
      */ 
1134
      
1135
      for (var i = 0; i < cln; i++)
1136
      {
1137
        PHY2D.joints[i].solve();
1138
      }
1139
      
1140
      /** 
1141
      * solve contacts
1142
      */ 
1143
      
1144
      for (var i = 0; i < len; i++)
1145
      {
1146
        PHY2D.contacts[i].solve();
1147
      }
1148
    }
1149
    
1150
    /** 
1151
    * integration
1152
    */ 
1153
1154
    for (var i = 0, len = PHY2D.objects.length; i < len; i++)
1155
    {
1156
      PHY2D.objects[i].integrate();
1157
    }
1158
    
1159
    /** 
1160
    * draw images
1161
    */ 
1162
1163
    for (var i = 0; i < len; i++)
1164
    {
1165
      var rb = PHY2D.objects[i];
1166
      rb.draw();
1167
    }
1168
    
1169
    /** 
1170
    * Manipulate object
1171
    */ 
1172
    
1173
    if (PHY2D.manipulate)
1174
    {
1175
      PHY2D.manipulate.solve();
1176
      ctx.beginPath();
1177
      ctx.lineWidth = 2;
1178
      ctx.setLineDash([2,2]);
1179
      ctx.moveTo(pointer.pos.x, pointer.pos.y);
1180
      ctx.lineTo(PHY2D.manipulate.pb.x, PHY2D.manipulate.pb.y);
1181
      ctx.strokeStyle = "rgb(255,255,255)";
1182
      ctx.stroke();
1183
      ctx.closePath();
1184
      ctx.fillStyle = "rgb(255,255,255)";
1185
      ctx.arc(PHY2D.manipulate.pb.x, PHY2D.manipulate.pb.y, 5, 0, 2 * Math.PI);
1186
      ctx.fill();
1187
    }
1188
  }
1189
  
1190
  /** 
1191
  * create new rectangle
1192
  */ 
1193
  
1194
  PHY2D.rectangle = function (x, y, w, h, mass, angle, img)
1195
  {
1196
    x *= this.zoom;
1197
    y *= this.zoom;
1198
    w *= this.zoom;
1199
    h *= this.zoom;
1200
    var vertices = [
1201
      w / 2, -h / 2, -w / 2, -h / 2, -w / 2, h / 2, w / 2, h / 2
1202
    ];
1203
    var rb = Object.create(PHY2D.Polygon).init(x, y, w, h, vertices, mass, angle, img);
1204
    PHY2D.objects.push(rb);
1205
    return rb;
1206
  }
1207
  
1208
  /** 
1209
  * create new triangle
1210
  */ 
1211
  
1212
  PHY2D.triangle = function (x, y, w, h, mass, angle, img)
1213
  {
1214
    x *= this.zoom;
1215
    y *= this.zoom;
1216
    w *= this.zoom;
1217
    h *= this.zoom;
1218
    var vertices = [
1219
      w / 2, h / 2, 0, - h / 2, -w / 2, h / 2
1220
    ];
1221
    var rb = Object.create(PHY2D.Polygon).init(x, y, w, h, vertices, mass, angle, img);
1222
    PHY2D.objects.push(rb);
1223
    return rb;
1224
  }
1225
  
1226
  /** 
1227
  * create new circle
1228
  */ 
1229
  
1230
  PHY2D.circle = function (x, y, w, mass, img)
1231
  {
1232
    x *= this.zoom;
1233
    y *= this.zoom;
1234
    w *= this.zoom;
1235
    var rb = Object.create(PHY2D.Circle).init(x, y, w, mass, img);
1236
    PHY2D.objects.push(rb);
1237
    return rb;
1238
  }
1239
  
1240
  /** 
1241
  * create new constraint
1242
  */ 
1243
  
1244
  PHY2D.addJoint = function (A, va, B, vb)
1245
  {
1246
    PHY2D.joints.push(
1247
      new PHY2D.Constraint(A, va, B, vb)
1248
    );
1249
  }
1250
  
1251
  /** 
1252
  * delete static objects
1253
  * used for screen resize
1254
  */ 
1255
  
1256
  PHY2D.deleteStatic = function ()
1257
  {
1258
    var k = PHY2D.objects.length;
1259
    while (k--)
1260
    {
1261
      var p = PHY2D.objects[k];
1262
      if (!p.invMass)
1263
      {
1264
        PHY2D.objects.splice(k, 1);
1265
      }
1266
    }
1267
  }
1268
  
1269
  /** 
1270
  * default engine zoom
1271
  */ 
1272
  
1273
  PHY2D.zoom = 1;
1274
  
1275
  /** 
1276
  * cancel manipulation
1277
  */ 
1278
  
1279
  PHY2D.stopManipulate = function () { PHY2D.manipulate = null; }
1280
  
1281
1282
  ////////////////////////////////////////////////////////////////
1283
1284
  screen.resize();
1285
  
1286
  for (var i = 0; i < 6; i++) PHY2D.circle(100 + i * 200, screen.height - 100, 200, 1, {src: "e6.png"});
1287
  for (var i = 1; i < 6; i++) PHY2D.circle(100 + i * 233, screen.height - 333, 233, 1, {src: "e8.png"});
1288
  PHY2D.rectangle(500, -400, 370, 420, 4, Math.PI / 2, {
1289
    src: "p25.png", 
1290
    x: 10, 
1291
    w: 380
1292
  });
1293
  
1294
  // ==== main loop ====
1295
  
1296
  function run()
1297
  {
1298
    requestAnimationFrame(run);
1299
    ctx.clearRect(0, 0, screen.width, screen.height);
1300
    PHY2D.render();
1301
  }
1302
  
1303
  // ==== start animation ====
1304
  run();
1305
  
1306
}();
必须是有效的URL
+ 添加另一个资源
Close

文件管理 点击文件查看URL

图片

  1. 暂无文件

CSS

  1. 暂无文件

JavaScript

  1. 暂无文件

其他

  1. 暂无文件
拖动文件到上面的区域或者:
加载中 ..................