@makefully/platypus 4.1.4 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +4 -2
  3. package/lib/platypus.js +1 -1
  4. package/package.json +7 -2
  5. package/src/AABB.js +685 -0
  6. package/src/ActionState.js +121 -0
  7. package/src/AssetManager.js +206 -0
  8. package/src/CollisionData.js +96 -0
  9. package/src/CollisionDataContainer.js +127 -0
  10. package/src/CollisionShape.js +315 -0
  11. package/src/Component.js +231 -0
  12. package/src/Data.js +64 -0
  13. package/src/DataMap.js +204 -0
  14. package/src/Entity.js +404 -0
  15. package/src/EntityLinker.js +113 -0
  16. package/src/EventHandler.js +115 -0
  17. package/src/EventHandlerList.js +251 -0
  18. package/src/Game.js +840 -0
  19. package/src/Gamepad.js +175 -0
  20. package/src/ID3CaptionPlayer.js +61 -0
  21. package/src/Messenger.js +365 -0
  22. package/src/PIXIAnimation.js +622 -0
  23. package/src/RandomSet.js +140 -0
  24. package/src/SFXPlayer.js +120 -0
  25. package/src/StateMap.js +252 -0
  26. package/src/Storage.js +152 -0
  27. package/src/TickerClient.js +48 -0
  28. package/src/TickerServer.js +23 -0
  29. package/src/TimeEvent.js +50 -0
  30. package/src/TimeEventList.js +100 -0
  31. package/src/VOPlayer.js +684 -0
  32. package/src/Vector.js +702 -0
  33. package/src/components/AIChaser.js +161 -0
  34. package/src/components/AIPacer.js +107 -0
  35. package/src/components/AudioMusic.js +151 -0
  36. package/src/components/AudioSFX.js +673 -0
  37. package/src/components/AudioVO.js +301 -0
  38. package/src/components/CollisionBasic.js +826 -0
  39. package/src/components/CollisionFilter.js +74 -0
  40. package/src/components/CollisionGroup.js +448 -0
  41. package/src/components/CollisionTiles.js +608 -0
  42. package/src/components/ComponentSwitcher.js +173 -0
  43. package/src/components/ControllerInput.js +65 -0
  44. package/src/components/Counter.js +165 -0
  45. package/src/components/EntityCamera.js +215 -0
  46. package/src/components/EntityContainer.js +549 -0
  47. package/src/components/EntityController.js +541 -0
  48. package/src/components/HandlerCamera.js +1052 -0
  49. package/src/components/HandlerCollision.js +1318 -0
  50. package/src/components/HandlerController.js +291 -0
  51. package/src/components/HandlerLogic.js +394 -0
  52. package/src/components/HandlerRender.js +217 -0
  53. package/src/components/Interactive.js +502 -0
  54. package/src/components/LevelBuilder.js +524 -0
  55. package/src/components/LogicAngularMovement.js +154 -0
  56. package/src/components/LogicAttachment.js +265 -0
  57. package/src/components/LogicButton.js +466 -0
  58. package/src/components/LogicCarrier.js +39 -0
  59. package/src/components/LogicDestroyMe.js +58 -0
  60. package/src/components/LogicDirectionalMovement.js +325 -0
  61. package/src/components/LogicDragDrop.js +371 -0
  62. package/src/components/LogicImpactLaunch.js +144 -0
  63. package/src/components/LogicPacingPlatform.js +64 -0
  64. package/src/components/LogicPortable.js +112 -0
  65. package/src/components/LogicPortal.js +115 -0
  66. package/src/components/LogicPushable.js +98 -0
  67. package/src/components/LogicRebounder.js +136 -0
  68. package/src/components/LogicRegionSpawner.js +139 -0
  69. package/src/components/LogicRotationalMovement.js +205 -0
  70. package/src/components/LogicSpawner.js +159 -0
  71. package/src/components/LogicStateMachine.js +382 -0
  72. package/src/components/LogicSwitch.js +77 -0
  73. package/src/components/LogicTeleportee.js +68 -0
  74. package/src/components/LogicTeleporter.js +93 -0
  75. package/src/components/LogicTimer.js +178 -0
  76. package/src/components/LogicWindUpRacer.js +179 -0
  77. package/src/components/Motion.js +371 -0
  78. package/src/components/Mover.js +566 -0
  79. package/src/components/Node.js +289 -0
  80. package/src/components/NodeMap.js +295 -0
  81. package/src/components/NodeResident.js +636 -0
  82. package/src/components/Orientation.js +697 -0
  83. package/src/components/RandomEvents.js +73 -0
  84. package/src/components/RelativePosition.js +204 -0
  85. package/src/components/RelayFamily.js +106 -0
  86. package/src/components/RelayGame.js +53 -0
  87. package/src/components/RelayLinker.js +94 -0
  88. package/src/components/RelayParent.js +57 -0
  89. package/src/components/RelaySelf.js +55 -0
  90. package/src/components/Render.js +178 -0
  91. package/src/components/RenderAnimator.js +275 -0
  92. package/src/components/RenderContainer.js +646 -0
  93. package/src/components/RenderDebug.js +319 -0
  94. package/src/components/RenderDestroyMe.js +59 -0
  95. package/src/components/RenderLottie.js +149 -0
  96. package/src/components/RenderProgress.js +120 -0
  97. package/src/components/RenderSpine.js +833 -0
  98. package/src/components/RenderSprite.js +527 -0
  99. package/src/components/RenderText.js +147 -0
  100. package/src/components/RenderTiles.js +1391 -0
  101. package/src/components/SceneChanger.js +128 -0
  102. package/src/components/TiledLoader.js +1451 -0
  103. package/src/components/Timeline.js +314 -0
  104. package/src/components/Tutorial.js +267 -0
  105. package/src/components/Tween.js +296 -0
  106. package/src/components/VoiceOver.js +373 -0
  107. package/src/components/XHR.js +129 -0
  108. package/src/components/index.js +79 -0
  109. package/src/factory.js +305 -0
  110. package/src/index.js +159 -0
  111. package/src/mixins/PIXI.Graphics.js +41 -0
  112. package/src/platypus.css +68 -0
  113. package/src/sayHello.js +50 -0
  114. package/src/shapes/Capsule.js +74 -0
  115. package/src/shapes/Circle.js +43 -0
  116. package/src/shapes/Ellipse.js +117 -0
  117. package/src/shapes/Point.js +191 -0
  118. package/src/shapes/Polygon.js +112 -0
  119. package/src/shapes/Polyline.js +97 -0
  120. package/src/shapes/Rectangle.js +78 -0
  121. package/src/shapes/RoundedRectangle.js +33 -0
  122. package/src/shapes/Segment.js +346 -0
  123. package/src/shapes/adjustedXY.js +19 -0
  124. package/src/shapes/castFromBox2D.js +92 -0
  125. package/src/shapes/castFromObject.js +53 -0
  126. package/src/shapes/castFromPixiGraphics.js +6 -0
  127. package/src/shapes/castFromPixiShape.js +89 -0
  128. package/src/shapes/castToBox2D.js +190 -0
  129. package/src/shapes/castToPixiGraphics.js +100 -0
  130. package/src/shapes/castToPixiShape.js +61 -0
  131. package/src/shapes/checkPointsMap.js +313 -0
  132. package/src/shapes/index.js +35 -0
  133. package/src/utils/array.js +73 -0
  134. package/src/utils/string.js +39 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * This adds support for a tiny API to PIXI.Graphics similar to the CreateJS Graphics API. This is used for backwards support for RenderSprite masks.
3
+ */
4
+ import {Graphics} from 'pixi.js';
5
+
6
+ export default (function () {
7
+ const gfx = Graphics.prototype;
8
+
9
+ gfx.a = gfx.a || gfx.arc;
10
+ gfx.at = gfx.at || gfx.arcTo;
11
+ gfx.bt = gfx.bt || gfx.bezierCurveTo;
12
+ gfx.c = gfx.c || gfx.clear;
13
+ gfx.dc = gfx.dc || gfx.circle;
14
+ gfx.de = gfx.de || gfx.ellipse;
15
+ gfx.dr = gfx.dr || gfx.rect;
16
+ gfx.ef = gfx.ef || gfx.endFill;
17
+ gfx.f = gfx.f || gfx.beginFill;
18
+ gfx.lt = gfx.lt || gfx.lineTo;
19
+ gfx.mt = gfx.mt || gfx.moveTo;
20
+ gfx.qt = gfx.qt || gfx.quadraticCurveTo;
21
+ gfx.r = gfx.r || gfx.rect;
22
+ gfx.rr = gfx.rr || gfx.roundRect;
23
+
24
+ /* Other CreateJS shortcuts that are unsupported by PIXI
25
+ gfx.cp
26
+ gfx.lf
27
+ gfx.rf
28
+ gfx.bf
29
+ gfx.ss
30
+ gfx.sd
31
+ gfx.s
32
+ gfx.ls
33
+ gfx.rs
34
+ gfx.bs
35
+ gfx.es
36
+ gfx.rc
37
+ gfx.dp
38
+ gfx.p
39
+ */
40
+
41
+ } ());
@@ -0,0 +1,68 @@
1
+ div#captions {
2
+ user-select: none;
3
+ -moz-user-select: none;
4
+ -webkit-user-select: none;
5
+ -ms-user-select: none;
6
+ -webkit-touch-callout: none;
7
+ }
8
+
9
+ canvas.platypus-canvas {
10
+ height: 100%;
11
+ position: relative;
12
+ width: 100%;
13
+
14
+ user-select: none;
15
+ -moz-user-select: none;
16
+ -webkit-user-select: none;
17
+ -ms-user-select: none;
18
+ -webkit-touch-callout: none;
19
+ }
20
+
21
+ .platypus-debugging {
22
+ position: fixed;
23
+ pointer-events: none;
24
+ color: #fff;
25
+ font-size: 12px;
26
+ padding: 5px;
27
+ background: rgba(0, 0, 0, 0.4);
28
+ z-index: 2;
29
+ font-family: "Lucida Console", Monaco, monospace;
30
+ -webkit-touch-callout: none;
31
+ user-select: none;
32
+ cursor: default;
33
+ margin: 0;
34
+ line-height: 1.5em;
35
+ }
36
+
37
+ #framerate {
38
+ top: 0;
39
+ left: 0;
40
+ }
41
+
42
+ #platypus-logging {
43
+ top: 0;
44
+ right: 0;
45
+ max-width: 40%;
46
+ max-height: 40%;
47
+ overflow-x: auto;
48
+ }
49
+
50
+ #platypus-logging .general {color: lightgreen;}
51
+ #platypus-logging .log {color: white;}
52
+ #platypus-logging .warn {color: yellow;}
53
+ #platypus-logging .debug {color: lightblue;}
54
+ #platypus-logging .error {color: lightcoral;}
55
+ #platypus-logging span {padding-right: 1.5em;}
56
+
57
+ #toggle-platypus-debugging {
58
+ position: fixed;
59
+ top: 0;
60
+ right: 0;
61
+ font-size: 12px;
62
+ padding: 5px;
63
+ background: rgba(0, 0, 0, 0.4);
64
+ font-family: "Lucida Console", Monaco, monospace;
65
+ border: none;
66
+ color: white;
67
+ z-index: 3;
68
+ }
@@ -0,0 +1,50 @@
1
+ /* global console, document, platypus */
2
+ import {VERSION as pixiVersion} from 'pixi.js';
3
+ import {arrayCache} from './utils/array.js';
4
+
5
+ const
6
+ getPortion = (num, min, max) => Math.floor(num * min / max),
7
+ getStyle = (title, version) => {
8
+ const
9
+ min = 204,
10
+ A = 65,
11
+ v = version?.split?.('.'),
12
+ useVersion = v?.length >= 3,
13
+ r = useVersion ? parseInt(v[0], 10) : (title.charCodeAt(0) || A) - A,
14
+ g = useVersion ? parseInt(v[1], 10) : (title.charCodeAt(1) || A) - A,
15
+ b = useVersion ? parseInt(v[2], 10) : (title.charCodeAt(2) || A) - A,
16
+ max = Math.max(r, g, b, 1);
17
+
18
+ return `color: #ffffff; line-height: 1.5em; border-radius: 6px; background-color: rgb(${getPortion(r, min, max)},${getPortion(g, min, max)},${getPortion(b, min, max)});`;
19
+ },
20
+ getVersions = (arr) => arr.reduce((arr, str) => {
21
+ arr.push(getStyle(str, str.substr(str.lastIndexOf(' ') - str.length + 1)), 'line-height: 1.5em;');
22
+ return arr;
23
+ }, []);
24
+
25
+ export default function (app) {
26
+ const
27
+ {options = {}} = app,
28
+ {version = '(?)'} = options,
29
+ docAuth = document.getElementsByName('author')?.[0]?.getAttribute('content') ?? '',
30
+ author = docAuth ? `by ${docAuth}` : '',
31
+ title = `${options.name ?? app.name ?? document.title ?? ''}${version ? ` ${version}` : ''}`,
32
+ using = arrayCache.setUp(),
33
+ {springroll, supports} = platypus;
34
+
35
+ if (springroll) {
36
+ using.push('SpringRoll ' + springroll.version);
37
+ }
38
+
39
+ using.push('Pixi.js ' + pixiVersion);
40
+
41
+ if (supports.firefox || supports.chrome) {
42
+ console.log(`%c ${title} %c ${author}`, getStyle(title, title.substr(title.lastIndexOf(' ') - title.length + 1)), 'line-height: 1.5em;');
43
+ using.push(`Platypus ${platypus.version}`);
44
+ console.log(`Using %c ${using.join(' %c %c ')} %c `, ...getVersions(using));
45
+ } else {
46
+ console.log(`--- "${title}" ${author} - Using ${using.join(', ')}, and Platypus ${platypus.version} ---`);
47
+ }
48
+
49
+ arrayCache.recycle(using);
50
+ };
@@ -0,0 +1,74 @@
1
+ import adjustedXY from "./adjustedXY";
2
+ import Circle from "./Circle";
3
+ import Rectangle from "./Rectangle";
4
+ import Segment from "./Segment";
5
+
6
+ const
7
+ axisInfo = {
8
+ x: {
9
+ axis: 'x',
10
+ alt: 'y',
11
+ length: 'width',
12
+ breadth: 'height'
13
+ },
14
+ y: {
15
+ axis: 'y',
16
+ alt: 'x',
17
+ length: 'height',
18
+ breadth: 'width'
19
+ }
20
+ };
21
+
22
+ export default class Capsule extends Segment {
23
+ constructor (...args) {
24
+ super(...args);
25
+ this.type = 'capsule';
26
+ }
27
+
28
+ initialize (options) {
29
+ if (!options.points && (!options.a || !options.b) && options.width && options.height) {
30
+ const
31
+ {alt, axis, breadth, length} = axisInfo[options.width > options.height ? 'x' : 'y'],
32
+ radius = options[breadth] / 2,
33
+ len = options[length] - 2 * radius,
34
+ hl = len / 2;
35
+
36
+ super.initialize({
37
+ ...options,
38
+ a: {
39
+ [axis]: -hl,
40
+ [alt]: 0
41
+ },
42
+ b: {
43
+ [axis]: hl,
44
+ [alt]: 0
45
+ }
46
+ });
47
+ this.radius = radius;
48
+ } else {
49
+ super.initialize(options);
50
+ this.radius = options.radius ?? 0;
51
+ }
52
+ }
53
+
54
+ duplicate () {
55
+ return new Capsule(this);
56
+ }
57
+
58
+ toObject (options) {
59
+ const
60
+ {a, b, radius, type} = this,
61
+ {scale = 1} = options;
62
+
63
+ return {
64
+ a: adjustedXY(a, options),
65
+ b: adjustedXY(b, options),
66
+ radius: radius * scale,
67
+ type
68
+ };
69
+ }
70
+
71
+ static validateDefinition (definition) {
72
+ return Rectangle.validateDefinition(definition) || (Circle.validateDefinition(definition) && Segment.validateDefinition(definition));
73
+ }
74
+ }
@@ -0,0 +1,43 @@
1
+ import adjustedXY from "./adjustedXY";
2
+ import Point from "./Point";
3
+
4
+ export default class Circle extends Point {
5
+ constructor (...args) {
6
+ super(...args);
7
+ this.type = 'circle';
8
+ }
9
+
10
+ initialize (options) {
11
+ super.initialize(options);
12
+ this.radius = options.radius ?? 0;
13
+ }
14
+
15
+ duplicate () {
16
+ return new Circle(this);
17
+ }
18
+
19
+ isPointInside ({x, y}, epsilon = 1e-6) {
20
+ const
21
+ {registration} = this,
22
+ dx = x - registration.x,
23
+ dy = y - registration.y;
24
+
25
+ return (dx * dx + dy * dy) <= (this.radius + epsilon) ** 2;
26
+ }
27
+
28
+ toObject (options) {
29
+ const
30
+ {radius, type} = this,
31
+ {scale = 1} = options;
32
+
33
+ return {
34
+ radius: radius * scale,
35
+ type,
36
+ ...adjustedXY(this, options)
37
+ };
38
+ }
39
+
40
+ static validateDefinition ({radius} = {}) {
41
+ return typeof radius === 'number' && radius > 0;
42
+ }
43
+ }
@@ -0,0 +1,117 @@
1
+ import adjustedXY from "./adjustedXY";
2
+ import Capsule from "./Capsule";
3
+ import Rectangle from "./Rectangle";
4
+ import Segment from "./Segment";
5
+
6
+ export default class Ellipse extends Segment {
7
+ constructor (...args) {
8
+ super(...args);
9
+ this.type = 'ellipse';
10
+ }
11
+
12
+ get halfHeight () {
13
+ return this.height / 2;
14
+ }
15
+
16
+ set halfHeight (hh) {
17
+ this.height = hh * 2;
18
+ }
19
+
20
+ get halfWidth () {
21
+ return this.width / 2;
22
+ }
23
+
24
+ set halfWidth (hw) {
25
+ this.width = hw * 2;
26
+ }
27
+
28
+ initialize (options) {
29
+ const
30
+ {a, b, height, points, width} = options;
31
+
32
+ if (!points && !(a && b)) {
33
+ const
34
+ hw = width / 2,
35
+ hh = height / 2;
36
+
37
+ if (width > height) {
38
+ const
39
+ c = Math.sqrt(hw * hw - hh * hh);
40
+
41
+ super.initialize({
42
+ ...options,
43
+ a: {x: -c, y: 0},
44
+ b: {x: c, y: 0}
45
+ });
46
+ } else {
47
+ const
48
+ c = Math.sqrt(hh * hh - hw * hw);
49
+
50
+ super.initialize({
51
+ ...options,
52
+ a: {x: 0, y: -c},
53
+ b: {x: 0, y: c}
54
+ });
55
+ }
56
+ } else {
57
+ super.initialize(options);
58
+ }
59
+ this.height = height;
60
+ this.width = width;
61
+ Object.defineProperties(this, {
62
+ constantSumOfDistances: {
63
+ get () {
64
+ return Math.max(this.width, this.height);
65
+ }
66
+ }
67
+ });
68
+
69
+ //TODO: accept rotation
70
+ }
71
+
72
+ toCapsule () {
73
+ const
74
+ {a, b, constantSumOfDistances, width, height} = this,
75
+ hw = width / 2,
76
+ hh = height / 2,
77
+ radius = (width > height) ? hh : hw,
78
+ eOffset = this.getLength(),
79
+ cOffset = constantSumOfDistances - radius - radius,
80
+ ratio = cOffset / eOffset;
81
+
82
+ return new Capsule({
83
+ a: {
84
+ x: a.x * ratio,
85
+ y: a.y * ratio
86
+ },
87
+ b: {
88
+ x: b.x * ratio,
89
+ y: b.y * ratio
90
+ },
91
+ radius
92
+ });
93
+ }
94
+
95
+ duplicate () {
96
+ return new Ellipse(this);
97
+ }
98
+
99
+ toObject (options) {
100
+ const
101
+ {a, b, height, radius, type, width} = this,
102
+ {scale = 1} = options;
103
+
104
+ return {
105
+ a: adjustedXY(a, options),
106
+ b: adjustedXY(b, options),
107
+ height: height * scale,
108
+ radius: radius * scale,
109
+ type,
110
+ width: width * scale
111
+ };
112
+ }
113
+
114
+ static validateDefinition (definition) {
115
+ return Rectangle.validateDefinition(definition);
116
+ }
117
+ }
@@ -0,0 +1,191 @@
1
+ /* global platypus */
2
+ import Vector from '../Vector';
3
+ import checkPointsMap from './checkPointsMap';
4
+ import adjustedXY from './adjustedXY';
5
+ import castToPixiShape from './castToPixiShape';
6
+ import castToPixiGraphics from './castToPixiGraphics';
7
+ import castToBox2D from './castToBox2D';
8
+
9
+ const
10
+ PI2 = Math.PI * 2;
11
+
12
+ export default class Point {
13
+ constructor (options) {
14
+ this.type = 'point';
15
+ this.registration = new Vector();
16
+ this.initialize(options);
17
+ }
18
+
19
+ get rotation () {
20
+ return (this.revolutions * PI2) % PI2;
21
+ }
22
+
23
+ set rotation (rotation) {
24
+ this.revolutions = rotation / PI2;
25
+ }
26
+
27
+ get x () {
28
+ return this.registration.x;
29
+ }
30
+
31
+ set x (x) {
32
+ this.moveX(x);
33
+ }
34
+
35
+ get y () {
36
+ return this.registration.y;
37
+ }
38
+
39
+ set y (y) {
40
+ this.moveY(y);
41
+ }
42
+
43
+ initialize ({revolutions, rotation = 0, x = 0, y = 0}) {
44
+ this.moveX(x);
45
+ this.moveY(y);
46
+ this.revolutions = revolutions ?? (rotation / PI2);
47
+ }
48
+
49
+ // "to" castings (toObject must be defined by inheriting classes)
50
+
51
+ toBox2D (...args) {
52
+ return castToBox2D(this, ...args);
53
+ }
54
+
55
+ toJSON (options, ...args) {
56
+ return JSON.stringify(this.toObject(options), ...args);
57
+ }
58
+
59
+ toObject (options) {
60
+ const
61
+ {type} = this;
62
+
63
+ return {
64
+ type,
65
+ ...adjustedXY(this, options)
66
+ };
67
+ }
68
+
69
+ toPixiShape (...args) {
70
+ return castToPixiShape(this, ...args);
71
+ }
72
+
73
+ toPixiGraphics (...args) {
74
+ return castToPixiGraphics(this, ...args);
75
+ }
76
+
77
+ move ({x, y}) {
78
+ this.moveX(x);
79
+ this.moveY(y);
80
+ }
81
+
82
+ moveX (x) {
83
+ this.registration.x = x;
84
+ }
85
+
86
+ moveY (y) {
87
+ this.registration.y = y;
88
+ }
89
+
90
+ shift ({x, y}) {
91
+ this.shiftX(x);
92
+ this.shiftY(y);
93
+ }
94
+
95
+ shiftX (x) {
96
+ this.moveX(this.x + x);
97
+ }
98
+
99
+ shiftY (y) {
100
+ this.moveY(this.y + y);
101
+ }
102
+
103
+ checkVisibility (shapes) {
104
+ const
105
+ segments = shapes.reduce((list, shape) => {
106
+ const
107
+ {segments} = shape;
108
+
109
+ if (segments) {
110
+ list.push(...segments);
111
+ } else {
112
+ list.push(shape)
113
+ }
114
+
115
+ return list;
116
+ }, []);
117
+
118
+ return segments.filter((seg) => {
119
+ // Midpoint of segment
120
+ const
121
+ mid = seg.a && seg.b ? {
122
+ x: (seg.a.x + seg.b.x) / 2,
123
+ y: (seg.a.y + seg.b.y) / 2
124
+ } : seg;
125
+
126
+ return !segments.some((blocker) => (seg !== blocker) && blocker.intersects?.({a: this, b: mid}));
127
+ });
128
+ }
129
+
130
+ getClosestPoints (shape, offset) {
131
+ let
132
+ closestPoints = null;
133
+
134
+ if (offset?.x ?? offset?.y) {
135
+ const
136
+ offsetShape = shape.duplicate();
137
+
138
+ offsetShape.shift(offset);
139
+ closestPoints = checkPointsMap[offsetShape.type]?.[this.type]?.(offsetShape, this) ?? null;
140
+ //TODO: discard/recycle shape
141
+ } else {
142
+ closestPoints = checkPointsMap[shape.type]?.[this.type]?.(shape, this) ?? null;
143
+ }
144
+
145
+ if (!closestPoints) {
146
+ platypus.debug.warn(`No closest point support for "${checkPointsMap[shape.type] ? this.type : shape.type}" shapes.`);
147
+ }
148
+
149
+ return closestPoints;
150
+ }
151
+
152
+ duplicate () {
153
+ return new Point(this);
154
+ }
155
+
156
+ getRadians () {
157
+ return this.revolutions * PI2;
158
+ }
159
+
160
+ getDegrees () {
161
+ return this.revolutions * 360;
162
+ }
163
+
164
+ getSinCos () {
165
+ const
166
+ {rotation} = this;
167
+
168
+ return {
169
+ sin: Math.sin(rotation),
170
+ cos: Math.cos(rotation)
171
+ };
172
+ }
173
+
174
+ toArray (local = false) {
175
+ if (local) {
176
+ const
177
+ {registration: {x, y}} = this;
178
+
179
+ return [x, y];
180
+ } else {
181
+ const
182
+ {x, y} = this;
183
+
184
+ return [x, y];
185
+ }
186
+ }
187
+
188
+ static validateDefinition ({x, y} = {}) {
189
+ return typeof x === 'number' && typeof y === 'number' && !isNaN(x) && !isNaN(y);
190
+ }
191
+ }
@@ -0,0 +1,112 @@
1
+ /* global platypus */
2
+ import Polyline from "./Polyline";
3
+
4
+ const
5
+ MIN_VERTICES = 3,
6
+ MAX_VERTICES = 8,
7
+ SHARED_VERTICES = 2;
8
+
9
+ export default class Polygon extends Polyline {
10
+ constructor (...args) {
11
+ super(...args);
12
+ this.type = 'polygon';
13
+ }
14
+
15
+ get loop () {
16
+ return true; // Unlike Polyline, this can never not be a loop.
17
+ }
18
+
19
+ set loop (value) {
20
+ if (!value) {
21
+ throw new Error('Polygon shapes are comprised of a loop of segments.');
22
+ }
23
+ }
24
+
25
+ initialize (options) {
26
+ super.initialize(options);
27
+ this._loop = true;
28
+ }
29
+
30
+ isPointInside ({x, y}) {
31
+ const
32
+ {points} = this;
33
+ let inside = false;
34
+
35
+ for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
36
+ const
37
+ a = points[i],
38
+ b = points[j];
39
+
40
+ if ((a.y > y) !== (b.y > y) && x < (b.x - a.x) * (y - a.y) / (b.y - a.y) + a.x) {
41
+ inside = !inside;
42
+ }
43
+ }
44
+ return inside;
45
+ }
46
+
47
+ duplicate () {
48
+ return new Polygon(this);
49
+ }
50
+
51
+ decompose () {
52
+ const
53
+ decomp = platypus.game.options.modules?.['poly-decomp'],
54
+ polygon = this.toArray2D(true);
55
+
56
+ if (decomp?.isSimple(polygon)) {
57
+ return decomp.quickDecomp(polygon).map((points) => new Polygon({
58
+ points
59
+ }));
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ split (min = MIN_VERTICES, max = MAX_VERTICES) {
66
+ const
67
+ breakAtMaxSize = min + max - SHARED_VERTICES,
68
+ arr = [],
69
+ convexPolygons = this.decompose() ?? [this]; // break into convex polygons if not already, since splitting a concave polygon could create polygon comprised of non-matching shapes.
70
+ let altSide = 0;
71
+
72
+ convexPolygons.forEach((polygon) => {
73
+ const
74
+ points = [...polygon.points];
75
+ let {length} = points;
76
+
77
+ while (length > max) {
78
+ const
79
+ count = (length >= breakAtMaxSize) ? max : Math.ceil(length / 2),
80
+ deleteCount = count - SHARED_VERTICES,
81
+ breakOff = points.splice((altSide - 1) * deleteCount, altSide ? deleteCount : Infinity);
82
+
83
+ breakOff.push(points[points.length - 1], points[0]);
84
+
85
+ arr.push(new Polygon({
86
+ points: breakOff
87
+ }));
88
+
89
+ altSide = 1 - altSide;
90
+ length = points.length;
91
+ }
92
+
93
+ arr.push(new Polygon({
94
+ points
95
+ }));
96
+ });
97
+
98
+ return arr;
99
+ }
100
+
101
+ static validateDefinition (definition = {}) {
102
+ const
103
+ {points} = definition,
104
+ {length = 0} = points ?? [];
105
+
106
+ if (length < 3) {
107
+ return false;
108
+ } else {
109
+ return Polyline.validateDefinition(definition);
110
+ }
111
+ }
112
+ }