@m2c2kit/core 0.3.14 → 0.3.16
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.
- package/dist/index.d.ts +34 -26
- package/dist/index.js +292 -133
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -726,12 +726,21 @@ interface CallbackOptions {
|
|
|
726
726
|
}
|
|
727
727
|
|
|
728
728
|
/**
|
|
729
|
-
*
|
|
729
|
+
* A Plugin is code that can be registered to run at certain points in the game loop.
|
|
730
730
|
*/
|
|
731
|
-
interface
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
731
|
+
interface Plugin {
|
|
732
|
+
/** Short identifier of the plugin. */
|
|
733
|
+
id: string;
|
|
734
|
+
/** What kind of m2c2kit object does the plugin work with? */
|
|
735
|
+
type: "Game" | "Session" | "Survey";
|
|
736
|
+
/** Initialization code run when the plugin is registered with the game. */
|
|
737
|
+
initialize?: (game: Game) => Promise<void>;
|
|
738
|
+
/** Is the plugin disabled and not to be run? Default is false. @remarks Disabled plugins will still be initialized. */
|
|
739
|
+
disabled?: boolean;
|
|
740
|
+
/** Plugin code run before the frame update, but before the frame draw. */
|
|
741
|
+
beforeUpdate?: (game: Game, deltaTime: number) => void;
|
|
742
|
+
/** Plugin code run after the frame update, but before the frame draw. */
|
|
743
|
+
afterUpdate?: (game: Game, deltaTime: number) => void;
|
|
735
744
|
}
|
|
736
745
|
|
|
737
746
|
interface TrialData {
|
|
@@ -757,6 +766,7 @@ declare class Game implements Activity {
|
|
|
757
766
|
private warmupFunctionQueue;
|
|
758
767
|
private loaderElementsRemoved;
|
|
759
768
|
private _dataStores?;
|
|
769
|
+
private plugins;
|
|
760
770
|
additionalParameters?: unknown;
|
|
761
771
|
staticTrialSchema: {
|
|
762
772
|
[key: string]: JsonSchemaDataTypeScriptTypes;
|
|
@@ -983,7 +993,7 @@ declare class Game implements Activity {
|
|
|
983
993
|
* is not found, then return fallback value
|
|
984
994
|
*
|
|
985
995
|
* @param parameterName - the name of the game parameter whose value is requested
|
|
986
|
-
* @param
|
|
996
|
+
* @param fallbackValue - the value to return if parameterName is not found
|
|
987
997
|
* @returns
|
|
988
998
|
*/
|
|
989
999
|
getParameterOrFallback<T, U>(parameterName: string, fallbackValue: U): T | U;
|
|
@@ -1171,12 +1181,14 @@ declare class Game implements Activity {
|
|
|
1171
1181
|
*/
|
|
1172
1182
|
private createOutgoingScene;
|
|
1173
1183
|
/**
|
|
1174
|
-
*
|
|
1184
|
+
* Registers a plugin with the game.
|
|
1175
1185
|
*
|
|
1176
|
-
* @
|
|
1177
|
-
*
|
|
1186
|
+
* @remarks Upon registration, the plugin's optional asynchronous
|
|
1187
|
+
* `initialize()` method will be called.
|
|
1188
|
+
*
|
|
1189
|
+
* @param plugin - Plugin to register
|
|
1178
1190
|
*/
|
|
1179
|
-
|
|
1191
|
+
registerPlugin(plugin: Plugin): Promise<void>;
|
|
1180
1192
|
private update;
|
|
1181
1193
|
private draw;
|
|
1182
1194
|
private calculateFps;
|
|
@@ -1224,17 +1236,6 @@ declare class Game implements Activity {
|
|
|
1224
1236
|
private htmlCanvasPointerDownHandler;
|
|
1225
1237
|
private htmlCanvasPointerUpHandler;
|
|
1226
1238
|
private htmlCanvasPointerMoveHandler;
|
|
1227
|
-
/**
|
|
1228
|
-
* Adjusts dragging behavior when the pointer leaves the canvas
|
|
1229
|
-
*
|
|
1230
|
-
* @remarks This is necessary because the pointerup event is not fired when
|
|
1231
|
-
* the pointer leaves the canvas. On desktop, this means that the user might
|
|
1232
|
-
* lift the pointer outside the canvas, but the entity will still be dragged
|
|
1233
|
-
* when the pointer is moved back into the canvas.
|
|
1234
|
-
*
|
|
1235
|
-
* @param domPointerEvent
|
|
1236
|
-
* @returns
|
|
1237
|
-
*/
|
|
1238
1239
|
private htmlCanvasPointerLeaveHandler;
|
|
1239
1240
|
/**
|
|
1240
1241
|
* Determines if/how m2c2kit entities respond to the DOM PointerDown event
|
|
@@ -1246,6 +1247,7 @@ declare class Game implements Activity {
|
|
|
1246
1247
|
private processDomPointerDown;
|
|
1247
1248
|
private processDomPointerUp;
|
|
1248
1249
|
private processDomPointerMove;
|
|
1250
|
+
private processDomPointerLeave;
|
|
1249
1251
|
private raiseM2PointerDownEvent;
|
|
1250
1252
|
private raiseTapDownEvent;
|
|
1251
1253
|
private raiseTapLeaveEvent;
|
|
@@ -1688,7 +1690,7 @@ interface EventBase {
|
|
|
1688
1690
|
/** Type of event. */
|
|
1689
1691
|
type: EventType | string;
|
|
1690
1692
|
/** The object on which the event occurred. */
|
|
1691
|
-
target: Entity | Session | Activity;
|
|
1693
|
+
target: Entity | Session | Activity | Plugin;
|
|
1692
1694
|
/** Has the event been taken care of by the listener and not be dispatched to other targets? */
|
|
1693
1695
|
handled?: boolean;
|
|
1694
1696
|
}
|
|
@@ -1794,10 +1796,10 @@ declare abstract class Entity implements EntityOptions {
|
|
|
1794
1796
|
isShape: boolean;
|
|
1795
1797
|
isText: boolean;
|
|
1796
1798
|
name: string;
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
+
_position: Point;
|
|
1800
|
+
_scale: number;
|
|
1799
1801
|
alpha: number;
|
|
1800
|
-
|
|
1802
|
+
_zRotation: number;
|
|
1801
1803
|
isUserInteractionEnabled: boolean;
|
|
1802
1804
|
draggable: boolean;
|
|
1803
1805
|
hidden: boolean;
|
|
@@ -2095,6 +2097,12 @@ declare abstract class Entity implements EntityOptions {
|
|
|
2095
2097
|
*/
|
|
2096
2098
|
get canvasKit(): CanvasKit;
|
|
2097
2099
|
get parentSceneAsEntity(): Entity;
|
|
2100
|
+
get position(): Point;
|
|
2101
|
+
set position(position: Point);
|
|
2102
|
+
get zRotation(): number;
|
|
2103
|
+
set zRotation(zRotation: number);
|
|
2104
|
+
get scale(): number;
|
|
2105
|
+
set scale(scale: number);
|
|
2098
2106
|
/**
|
|
2099
2107
|
* For a given directed acyclic graph, topological ordering of the vertices will be identified using BFS
|
|
2100
2108
|
* @param adjList Adjacency List that represent a graph with vertices and edges
|
|
@@ -3263,4 +3271,4 @@ declare class WebGlInfo {
|
|
|
3263
3271
|
static dispose(): void;
|
|
3264
3272
|
}
|
|
3265
3273
|
|
|
3266
|
-
export { Action, type Activity, type ActivityKeyValueData, type ActivityLifecycleEvent, type ActivityResultsEvent, ActivityType, type BrowserImage, type CallbackOptions, CanvasKitHelpers, ColorfulMutablePath, Composite, type CompositeOptions, Constants, ConstraintType, type Constraints, CustomAction, type CustomActionOptions, type DefaultParameter, Dimensions, type DrawableOptions, type EasingFunction, Easings, Entity, type EntityEvent, type EntityEventListener, type EntityOptions, EntityType, Equals, type EventBase, type EventListenerBase, EventType, FadeAlphaAction, type FadeAlphaActionOptions, type FontData, FontManager, Game, type GameData, type GameOptions, type GameParameters, GlobalVariables, type GoToActivityOptions, GroupAction, I18n, type IDataStore, type IDrawable, type IText, ImageManager, Label, LabelHorizontalAlignmentMode, type LabelOptions, type Layout, LayoutConstraint, LoadedImage, type M2ColorfulPath, type M2DragEvent, type M2Path, type M2PointerEvent, MoveAction, type MoveActionOptions, MutablePath, NoneTransition, type Point, RandomDraws, type RectOptions, type RgbaColor, RotateAction, ScaleAction, type ScaleActionOptions, Scene, type SceneOptions, SceneTransition, SequenceAction, Session, type SessionDictionaryValues, type SessionLifecycleEvent, type SessionOptions, Shape, type ShapeOptions, ShapeType, type Size, SlideTransition, type SlideTransitionOptions, Sprite, type SpriteOptions, Story, type StoryOptions, type TapEvent, TextLine, type TextLineOptions, type TextOptions, Timer, Transition, TransitionDirection, TransitionType, type Translations, type TrialData, type TrialSchema, Uuid, WaitAction, type WaitActionOptions, WebColors, WebGlInfo, handleInterfaceOptions };
|
|
3274
|
+
export { Action, type Activity, type ActivityKeyValueData, type ActivityLifecycleEvent, type ActivityResultsEvent, ActivityType, type BrowserImage, type CallbackOptions, CanvasKitHelpers, ColorfulMutablePath, Composite, type CompositeOptions, Constants, ConstraintType, type Constraints, CustomAction, type CustomActionOptions, type DefaultParameter, Dimensions, type DrawableOptions, type EasingFunction, Easings, Entity, type EntityEvent, type EntityEventListener, type EntityOptions, EntityType, Equals, type EventBase, type EventListenerBase, EventType, FadeAlphaAction, type FadeAlphaActionOptions, type FontData, FontManager, Game, type GameData, type GameOptions, type GameParameters, GlobalVariables, type GoToActivityOptions, GroupAction, I18n, type IDataStore, type IDrawable, type IText, ImageManager, Label, LabelHorizontalAlignmentMode, type LabelOptions, type Layout, LayoutConstraint, LoadedImage, type M2ColorfulPath, type M2DragEvent, type M2Path, type M2PointerEvent, MoveAction, type MoveActionOptions, MutablePath, NoneTransition, type Plugin, type Point, RandomDraws, type RectOptions, type RgbaColor, RotateAction, ScaleAction, type ScaleActionOptions, Scene, type SceneOptions, SceneTransition, SequenceAction, Session, type SessionDictionaryValues, type SessionLifecycleEvent, type SessionOptions, Shape, type ShapeOptions, ShapeType, type Size, SlideTransition, type SlideTransitionOptions, Sprite, type SpriteOptions, Story, type StoryOptions, type TapEvent, TextLine, type TextLineOptions, type TextOptions, Timer, Transition, TransitionDirection, TransitionType, type Translations, type TrialData, type TrialSchema, Uuid, WaitAction, type WaitActionOptions, WebColors, WebGlInfo, handleInterfaceOptions };
|
package/dist/index.js
CHANGED
|
@@ -175,7 +175,7 @@ class M2c2KitHelpers {
|
|
|
175
175
|
entities.push(drawableEntity);
|
|
176
176
|
const entityPointsArray = entities.map((entity) => {
|
|
177
177
|
const boundingBox = M2c2KitHelpers.calculateEntityAbsoluteBoundingBox(entity);
|
|
178
|
-
return boundingBoxToPoints(boundingBox);
|
|
178
|
+
return M2c2KitHelpers.boundingBoxToPoints(boundingBox);
|
|
179
179
|
});
|
|
180
180
|
for (let i = 0; i < entityPointsArray.length; i++) {
|
|
181
181
|
if (!entityNeedsRotation(entities[i])) {
|
|
@@ -183,7 +183,7 @@ class M2c2KitHelpers {
|
|
|
183
183
|
}
|
|
184
184
|
const entityPoints = entityPointsArray[i];
|
|
185
185
|
const radians = entities[i].zRotation;
|
|
186
|
-
const center = findCentroid(entityPoints);
|
|
186
|
+
const center = M2c2KitHelpers.findCentroid(entityPoints);
|
|
187
187
|
for (let j = i; j < entities.length; j++) {
|
|
188
188
|
entityPointsArray[j] = rotateRectangle(
|
|
189
189
|
entityPointsArray[j],
|
|
@@ -206,7 +206,7 @@ class M2c2KitHelpers {
|
|
|
206
206
|
* @param drawableEntity - Entity to rotate the canvas for
|
|
207
207
|
*/
|
|
208
208
|
static rotateCanvasForDrawableEntity(canvas, drawableEntity) {
|
|
209
|
-
const rotationTransforms = calculateRotationTransforms(drawableEntity);
|
|
209
|
+
const rotationTransforms = M2c2KitHelpers.calculateRotationTransforms(drawableEntity);
|
|
210
210
|
if (rotationTransforms.length === 0) {
|
|
211
211
|
return;
|
|
212
212
|
}
|
|
@@ -269,24 +269,167 @@ class M2c2KitHelpers {
|
|
|
269
269
|
}
|
|
270
270
|
return normalized;
|
|
271
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Checks if two points are on the same side of a line.
|
|
274
|
+
*
|
|
275
|
+
* @remarks The line is defined by two points, a and b. The function uses
|
|
276
|
+
* the cross product to determine the relative position of the points.
|
|
277
|
+
*
|
|
278
|
+
* @param p1 - point to check
|
|
279
|
+
* @param p2 - point to check
|
|
280
|
+
* @param a - point that defines one end of the line
|
|
281
|
+
* @param b - point that defines the other end of the line
|
|
282
|
+
* @returns true if p1 and p2 are on the same side of the line, or false
|
|
283
|
+
* otherwise
|
|
284
|
+
*/
|
|
285
|
+
static arePointsOnSameSideOfLine(p1, p2, a, b) {
|
|
286
|
+
const cp1 = (b.x - a.x) * (p1.y - a.y) - (b.y - a.y) * (p1.x - a.x);
|
|
287
|
+
const cp2 = (b.x - a.x) * (p2.y - a.y) - (b.y - a.y) * (p2.x - a.x);
|
|
288
|
+
return cp1 * cp2 >= 0;
|
|
289
|
+
}
|
|
272
290
|
/**
|
|
273
291
|
* Checks if a point is inside a rectangle.
|
|
274
292
|
*
|
|
293
|
+
* @remarks The rectangle may have been rotated (sides might not be parallel
|
|
294
|
+
* to the axes).
|
|
295
|
+
*
|
|
275
296
|
* @param point - The Point to check
|
|
276
297
|
* @param rect - An array of four Points representing the vertices of the
|
|
277
298
|
* rectangle in clockwise order
|
|
278
299
|
* @returns true if the Point is inside the rectangle
|
|
279
300
|
*/
|
|
280
301
|
static isPointInsideRectangle(point, rect) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
302
|
+
if (rect.length !== 4) {
|
|
303
|
+
throw new Error("Invalid input: expected an array of four points");
|
|
304
|
+
}
|
|
305
|
+
return M2c2KitHelpers.arePointsOnSameSideOfLine(
|
|
306
|
+
point,
|
|
307
|
+
rect[2],
|
|
308
|
+
rect[0],
|
|
309
|
+
rect[1]
|
|
310
|
+
) && M2c2KitHelpers.arePointsOnSameSideOfLine(
|
|
311
|
+
point,
|
|
312
|
+
rect[3],
|
|
313
|
+
rect[1],
|
|
314
|
+
rect[2]
|
|
315
|
+
) && M2c2KitHelpers.arePointsOnSameSideOfLine(
|
|
316
|
+
point,
|
|
317
|
+
rect[0],
|
|
318
|
+
rect[2],
|
|
319
|
+
rect[3]
|
|
320
|
+
) && M2c2KitHelpers.arePointsOnSameSideOfLine(point, rect[1], rect[3], rect[0]);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Checks if the entity or any of its ancestors have been rotated.
|
|
324
|
+
*
|
|
325
|
+
* @param entity - entity to check
|
|
326
|
+
* @returns true if the entity or any of its ancestors have been rotated
|
|
327
|
+
*/
|
|
328
|
+
static entityOrAncestorHasBeenRotated(entity) {
|
|
329
|
+
const entities = entity.ancestors;
|
|
330
|
+
entities.push(entity);
|
|
331
|
+
return entities.some((entity2) => entityNeedsRotation(entity2));
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Converts a bounding box to an array of four points representing the
|
|
335
|
+
* vertices of the rectangle.
|
|
336
|
+
*
|
|
337
|
+
* @remarks In m2c2kit, the y-axis is inverted: origin is in the upper-left.
|
|
338
|
+
* Vertices are returned in clockwise order starting from the upper-left.
|
|
339
|
+
*
|
|
340
|
+
* @param boundingBox
|
|
341
|
+
* @returns An array of four points
|
|
342
|
+
*/
|
|
343
|
+
static boundingBoxToPoints(boundingBox) {
|
|
344
|
+
const { xMin, xMax, yMin, yMax } = boundingBox;
|
|
345
|
+
const points = [
|
|
346
|
+
{ x: xMin, y: yMin },
|
|
347
|
+
// Top left
|
|
348
|
+
{ x: xMax, y: yMin },
|
|
349
|
+
// Top right
|
|
350
|
+
{ x: xMax, y: yMax },
|
|
351
|
+
// Bottom right
|
|
352
|
+
{ x: xMin, y: yMax }
|
|
353
|
+
// Bottom left
|
|
354
|
+
];
|
|
355
|
+
return points;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Finds the centroid of a rectangle.
|
|
359
|
+
*
|
|
360
|
+
* @param points - An array of four points representing the vertices of the
|
|
361
|
+
* rectangle.
|
|
362
|
+
* @returns array of points representing the centroid of the rectangle.
|
|
363
|
+
*/
|
|
364
|
+
static findCentroid(points) {
|
|
365
|
+
if (points.length !== 4) {
|
|
366
|
+
throw new Error("Invalid input: expected an array of four points");
|
|
288
367
|
}
|
|
289
|
-
|
|
368
|
+
let xSum = 0;
|
|
369
|
+
let ySum = 0;
|
|
370
|
+
for (const point of points) {
|
|
371
|
+
xSum += point.x;
|
|
372
|
+
ySum += point.y;
|
|
373
|
+
}
|
|
374
|
+
const xAvg = xSum / 4;
|
|
375
|
+
const yAvg = ySum / 4;
|
|
376
|
+
return { x: xAvg, y: yAvg };
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Rotates a point, counterclockwise, around another point by an angle in
|
|
380
|
+
* radians.
|
|
381
|
+
*
|
|
382
|
+
* @param point - Point to rotate
|
|
383
|
+
* @param radians - angle in radians
|
|
384
|
+
* @param center - Point to rotate around
|
|
385
|
+
* @returns rotated point
|
|
386
|
+
*/
|
|
387
|
+
static rotatePoint(point, radians, center) {
|
|
388
|
+
const dx = point.x - center.x;
|
|
389
|
+
const dy = point.y - center.y;
|
|
390
|
+
const x = dx * Math.cos(-radians) - dy * Math.sin(-radians);
|
|
391
|
+
const y = dx * Math.sin(-radians) + dy * Math.cos(-radians);
|
|
392
|
+
return {
|
|
393
|
+
x: x + center.x,
|
|
394
|
+
y: y + center.y
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Calculates the rotation transforms to apply to entity, respecting any
|
|
399
|
+
* ancestor rotations.
|
|
400
|
+
*
|
|
401
|
+
* @param drawableEntity - entity to calculate rotation transforms for
|
|
402
|
+
* @returns array of rotation transforms to apply
|
|
403
|
+
*/
|
|
404
|
+
static calculateRotationTransforms(drawableEntity) {
|
|
405
|
+
const rotationTransforms = [];
|
|
406
|
+
const entities = drawableEntity.ancestors;
|
|
407
|
+
entities.reverse();
|
|
408
|
+
entities.push(drawableEntity);
|
|
409
|
+
entities.forEach((entity) => {
|
|
410
|
+
if (entityNeedsRotation(entity)) {
|
|
411
|
+
const drawable = entity;
|
|
412
|
+
if (drawable.type === EntityType.Scene) {
|
|
413
|
+
const center2 = {
|
|
414
|
+
x: drawable.absolutePosition.x + drawable.size.width * 0.5,
|
|
415
|
+
y: drawable.absolutePosition.y + drawable.size.height * 0.5
|
|
416
|
+
};
|
|
417
|
+
rotationTransforms.push({
|
|
418
|
+
radians: drawable.zRotation,
|
|
419
|
+
center: center2
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const boundingBox = M2c2KitHelpers.calculateEntityAbsoluteBoundingBox(drawable);
|
|
424
|
+
const points = M2c2KitHelpers.boundingBoxToPoints(boundingBox);
|
|
425
|
+
const center = M2c2KitHelpers.findCentroid(points);
|
|
426
|
+
rotationTransforms.push({
|
|
427
|
+
radians: drawable.zRotation,
|
|
428
|
+
center
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
return rotationTransforms;
|
|
290
433
|
}
|
|
291
434
|
}
|
|
292
435
|
function applyRotationTransformsToCanvas(rotationTransforms, scale, canvas) {
|
|
@@ -298,84 +441,16 @@ function applyRotationTransformsToCanvas(rotationTransforms, scale, canvas) {
|
|
|
298
441
|
);
|
|
299
442
|
});
|
|
300
443
|
}
|
|
301
|
-
function calculateRotationTransforms(drawableEntity) {
|
|
302
|
-
const rotationTransforms = [];
|
|
303
|
-
const entities = drawableEntity.ancestors;
|
|
304
|
-
entities.reverse();
|
|
305
|
-
entities.push(drawableEntity);
|
|
306
|
-
entities.forEach((entity) => {
|
|
307
|
-
if (entityNeedsRotation(entity)) {
|
|
308
|
-
const drawable = entity;
|
|
309
|
-
if (drawable.type === EntityType.Scene) {
|
|
310
|
-
const center2 = {
|
|
311
|
-
x: drawable.absolutePosition.x + drawable.size.width * 0.5,
|
|
312
|
-
y: drawable.absolutePosition.y + drawable.size.height * 0.5
|
|
313
|
-
};
|
|
314
|
-
rotationTransforms.push({
|
|
315
|
-
radians: drawable.zRotation,
|
|
316
|
-
center: center2
|
|
317
|
-
});
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const boundingBox = M2c2KitHelpers.calculateEntityAbsoluteBoundingBox(drawable);
|
|
321
|
-
const points = boundingBoxToPoints(boundingBox);
|
|
322
|
-
const center = findCentroid(points);
|
|
323
|
-
rotationTransforms.push({
|
|
324
|
-
radians: drawable.zRotation,
|
|
325
|
-
center
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
return rotationTransforms;
|
|
330
|
-
}
|
|
331
444
|
function entityNeedsRotation(entity) {
|
|
332
445
|
return M2c2KitHelpers.normalizeAngleRadians(entity.zRotation) !== 0 && entity.isDrawable;
|
|
333
446
|
}
|
|
334
|
-
function boundingBoxToPoints(boundingBox) {
|
|
335
|
-
const { xMin, xMax, yMin, yMax } = boundingBox;
|
|
336
|
-
const points = [
|
|
337
|
-
{ x: xMin, y: yMin },
|
|
338
|
-
// Top left
|
|
339
|
-
{ x: xMin, y: yMax },
|
|
340
|
-
// Bottom left
|
|
341
|
-
{ x: xMax, y: yMax },
|
|
342
|
-
// Bottom right
|
|
343
|
-
{ x: xMax, y: yMin }
|
|
344
|
-
// Top right
|
|
345
|
-
];
|
|
346
|
-
return points;
|
|
347
|
-
}
|
|
348
|
-
function findCentroid(points) {
|
|
349
|
-
if (points.length !== 4) {
|
|
350
|
-
throw new Error("Invalid input: expected an array of four points");
|
|
351
|
-
}
|
|
352
|
-
let xSum = 0;
|
|
353
|
-
let ySum = 0;
|
|
354
|
-
for (const point of points) {
|
|
355
|
-
xSum += point.x;
|
|
356
|
-
ySum += point.y;
|
|
357
|
-
}
|
|
358
|
-
const xAvg = xSum / 4;
|
|
359
|
-
const yAvg = ySum / 4;
|
|
360
|
-
return { x: xAvg, y: yAvg };
|
|
361
|
-
}
|
|
362
|
-
function rotatePoint(point, radians, center) {
|
|
363
|
-
const dx = point.x - center.x;
|
|
364
|
-
const dy = point.y - center.y;
|
|
365
|
-
const x = dx * Math.cos(-radians) - dy * Math.sin(-radians);
|
|
366
|
-
const y = dx * Math.sin(-radians) + dy * Math.cos(-radians);
|
|
367
|
-
return {
|
|
368
|
-
x: x + center.x,
|
|
369
|
-
y: y + center.y
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
447
|
function rotateRectangle(rect, radians, center) {
|
|
373
448
|
if (rect.length !== 4) {
|
|
374
449
|
throw new Error("Invalid input: expected an array of four points");
|
|
375
450
|
}
|
|
376
451
|
const rotated = [];
|
|
377
452
|
for (const p of rect) {
|
|
378
|
-
rotated.push(rotatePoint(p, radians, center));
|
|
453
|
+
rotated.push(M2c2KitHelpers.rotatePoint(p, radians, center));
|
|
379
454
|
}
|
|
380
455
|
return rotated;
|
|
381
456
|
}
|
|
@@ -1440,11 +1515,11 @@ class Entity {
|
|
|
1440
1515
|
this.isDrawable = false;
|
|
1441
1516
|
this.isShape = false;
|
|
1442
1517
|
this.isText = false;
|
|
1443
|
-
this.
|
|
1518
|
+
this._position = { x: 0, y: 0 };
|
|
1444
1519
|
// position of the entity in the parent coordinate system
|
|
1445
|
-
this.
|
|
1520
|
+
this._scale = 1;
|
|
1446
1521
|
this.alpha = 1;
|
|
1447
|
-
this.
|
|
1522
|
+
this._zRotation = 0;
|
|
1448
1523
|
this.isUserInteractionEnabled = false;
|
|
1449
1524
|
this.draggable = false;
|
|
1450
1525
|
this.hidden = false;
|
|
@@ -2277,6 +2352,38 @@ class Entity {
|
|
|
2277
2352
|
}
|
|
2278
2353
|
throw new Error(`Entity ${this} has not been added to a scene`);
|
|
2279
2354
|
}
|
|
2355
|
+
get position() {
|
|
2356
|
+
const entity = this;
|
|
2357
|
+
return {
|
|
2358
|
+
get x() {
|
|
2359
|
+
return entity._position.x;
|
|
2360
|
+
},
|
|
2361
|
+
set x(x) {
|
|
2362
|
+
entity._position.x = x;
|
|
2363
|
+
},
|
|
2364
|
+
get y() {
|
|
2365
|
+
return entity._position.y;
|
|
2366
|
+
},
|
|
2367
|
+
set y(y) {
|
|
2368
|
+
entity._position.y = y;
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
set position(position) {
|
|
2373
|
+
this._position = position;
|
|
2374
|
+
}
|
|
2375
|
+
get zRotation() {
|
|
2376
|
+
return this._zRotation;
|
|
2377
|
+
}
|
|
2378
|
+
set zRotation(zRotation) {
|
|
2379
|
+
this._zRotation = zRotation;
|
|
2380
|
+
}
|
|
2381
|
+
get scale() {
|
|
2382
|
+
return this._scale;
|
|
2383
|
+
}
|
|
2384
|
+
set scale(scale) {
|
|
2385
|
+
this._scale = scale;
|
|
2386
|
+
}
|
|
2280
2387
|
// from https://medium.com/@konduruharish/topological-sort-in-typescript-and-c-6d5ecc4bad95
|
|
2281
2388
|
/**
|
|
2282
2389
|
* For a given directed acyclic graph, topological ordering of the vertices will be identified using BFS
|
|
@@ -3595,6 +3702,7 @@ class Game {
|
|
|
3595
3702
|
this.steppingNow = 0;
|
|
3596
3703
|
this.warmupFunctionQueue = new Array();
|
|
3597
3704
|
this.loaderElementsRemoved = false;
|
|
3705
|
+
this.plugins = [];
|
|
3598
3706
|
this.staticTrialSchema = {};
|
|
3599
3707
|
this.data = {
|
|
3600
3708
|
trials: new Array()
|
|
@@ -4042,7 +4150,7 @@ class Game {
|
|
|
4042
4150
|
* is not found, then return fallback value
|
|
4043
4151
|
*
|
|
4044
4152
|
* @param parameterName - the name of the game parameter whose value is requested
|
|
4045
|
-
* @param
|
|
4153
|
+
* @param fallbackValue - the value to return if parameterName is not found
|
|
4046
4154
|
* @returns
|
|
4047
4155
|
*/
|
|
4048
4156
|
getParameterOrFallback(parameterName, fallbackValue) {
|
|
@@ -5037,27 +5145,34 @@ class Game {
|
|
|
5037
5145
|
return outgoingScene;
|
|
5038
5146
|
}
|
|
5039
5147
|
/**
|
|
5040
|
-
*
|
|
5148
|
+
* Registers a plugin with the game.
|
|
5041
5149
|
*
|
|
5042
|
-
* @
|
|
5043
|
-
*
|
|
5150
|
+
* @remarks Upon registration, the plugin's optional asynchronous
|
|
5151
|
+
* `initialize()` method will be called.
|
|
5152
|
+
*
|
|
5153
|
+
* @param plugin - Plugin to register
|
|
5044
5154
|
*/
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5155
|
+
async registerPlugin(plugin) {
|
|
5156
|
+
if (plugin.type !== ActivityType.Game) {
|
|
5157
|
+
throw new Error(
|
|
5158
|
+
`registerPlugin(): plugin ${plugin.id} is not a game plugin. It is a ${plugin.type} plugin.`
|
|
5159
|
+
);
|
|
5160
|
+
}
|
|
5161
|
+
if (this.plugins.includes(plugin) || this.plugins.map((p) => p.id).includes(plugin.id)) {
|
|
5162
|
+
throw new Error(
|
|
5163
|
+
`registerPlugin(): plugin ${plugin.id} already registered.`
|
|
5164
|
+
);
|
|
5165
|
+
}
|
|
5166
|
+
this.plugins.push(plugin);
|
|
5167
|
+
if (plugin.initialize) {
|
|
5168
|
+
await plugin.initialize(this);
|
|
5169
|
+
}
|
|
5051
5170
|
}
|
|
5052
5171
|
update() {
|
|
5172
|
+
this.plugins.filter((p) => p.beforeUpdate !== void 0 && p.disabled !== true).forEach((p) => p.beforeUpdate(this, Globals.deltaTime));
|
|
5053
5173
|
this.scenes.filter((scene) => scene._active).forEach((scene) => scene.update());
|
|
5054
5174
|
this.freeEntitiesScene.update();
|
|
5055
|
-
|
|
5056
|
-
target: this,
|
|
5057
|
-
type: EventType.FrameDidSimulatePhysics,
|
|
5058
|
-
deltaTime: Globals.deltaTime
|
|
5059
|
-
};
|
|
5060
|
-
this.raiseActivityEventOnListeners(frameDidSimulatePhysicsEvent);
|
|
5175
|
+
this.plugins.filter((p) => p.afterUpdate !== void 0 && p.disabled !== true).forEach((p) => p.afterUpdate(this, Globals.deltaTime));
|
|
5061
5176
|
}
|
|
5062
5177
|
draw(canvas) {
|
|
5063
5178
|
this.scenes.filter((scene) => scene._active).forEach((scene) => scene.draw(canvas));
|
|
@@ -5471,35 +5586,26 @@ class Game {
|
|
|
5471
5586
|
domPointerEvent
|
|
5472
5587
|
);
|
|
5473
5588
|
}
|
|
5474
|
-
/**
|
|
5475
|
-
* Adjusts dragging behavior when the pointer leaves the canvas
|
|
5476
|
-
*
|
|
5477
|
-
* @remarks This is necessary because the pointerup event is not fired when
|
|
5478
|
-
* the pointer leaves the canvas. On desktop, this means that the user might
|
|
5479
|
-
* lift the pointer outside the canvas, but the entity will still be dragged
|
|
5480
|
-
* when the pointer is moved back into the canvas.
|
|
5481
|
-
*
|
|
5482
|
-
* @param domPointerEvent
|
|
5483
|
-
* @returns
|
|
5484
|
-
*/
|
|
5485
5589
|
htmlCanvasPointerLeaveHandler(domPointerEvent) {
|
|
5486
5590
|
if (!this.currentScene) {
|
|
5487
5591
|
return;
|
|
5488
5592
|
}
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5593
|
+
domPointerEvent.preventDefault();
|
|
5594
|
+
const scene = this.currentScene;
|
|
5595
|
+
if (!scene || !this.sceneCanReceiveUserInteraction(scene)) {
|
|
5596
|
+
return;
|
|
5597
|
+
}
|
|
5598
|
+
const m2Event = {
|
|
5599
|
+
target: scene,
|
|
5600
|
+
type: EventType.PointerLeave,
|
|
5601
|
+
handled: false
|
|
5602
|
+
};
|
|
5603
|
+
this.processDomPointerLeave(scene, m2Event, domPointerEvent);
|
|
5604
|
+
this.processDomPointerLeave(
|
|
5605
|
+
this.freeEntitiesScene,
|
|
5606
|
+
m2Event,
|
|
5607
|
+
domPointerEvent
|
|
5608
|
+
);
|
|
5503
5609
|
}
|
|
5504
5610
|
/**
|
|
5505
5611
|
* Determines if/how m2c2kit entities respond to the DOM PointerDown event
|
|
@@ -5612,7 +5718,7 @@ class Game {
|
|
|
5612
5718
|
entity.pressedAndWithinHitArea = false;
|
|
5613
5719
|
this.raiseTapLeaveEvent(entity, m2Event, domPointerEvent);
|
|
5614
5720
|
}
|
|
5615
|
-
if (this.IsCanvasPointWithinEntityBounds(
|
|
5721
|
+
if (entity.isUserInteractionEnabled && this.IsCanvasPointWithinEntityBounds(
|
|
5616
5722
|
entity,
|
|
5617
5723
|
domPointerEvent.offsetX,
|
|
5618
5724
|
domPointerEvent.offsetY
|
|
@@ -5620,15 +5726,13 @@ class Game {
|
|
|
5620
5726
|
this.raiseM2PointerMoveEvent(entity, m2Event, domPointerEvent);
|
|
5621
5727
|
entity.withinHitArea = true;
|
|
5622
5728
|
}
|
|
5623
|
-
if (!this.IsCanvasPointWithinEntityBounds(
|
|
5729
|
+
if (entity.isUserInteractionEnabled && entity.withinHitArea && !this.IsCanvasPointWithinEntityBounds(
|
|
5624
5730
|
entity,
|
|
5625
5731
|
domPointerEvent.offsetX,
|
|
5626
5732
|
domPointerEvent.offsetY
|
|
5627
5733
|
)) {
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
entity.withinHitArea = false;
|
|
5631
|
-
}
|
|
5734
|
+
entity.withinHitArea = false;
|
|
5735
|
+
this.raiseM2PointerLeaveEvent(entity, m2Event, domPointerEvent);
|
|
5632
5736
|
}
|
|
5633
5737
|
if (entity.children) {
|
|
5634
5738
|
entity.children.filter((entity2) => !entity2.hidden).filter((entity2) => entity2.isDrawable).sort(
|
|
@@ -5638,6 +5742,46 @@ class Game {
|
|
|
5638
5742
|
);
|
|
5639
5743
|
}
|
|
5640
5744
|
}
|
|
5745
|
+
processDomPointerLeave(entity, m2Event, domPointerEvent) {
|
|
5746
|
+
if (m2Event.handled) {
|
|
5747
|
+
return;
|
|
5748
|
+
}
|
|
5749
|
+
if (entity.dragging) {
|
|
5750
|
+
const m2Event2 = {
|
|
5751
|
+
target: entity,
|
|
5752
|
+
type: EventType.DragEnd,
|
|
5753
|
+
handled: false
|
|
5754
|
+
};
|
|
5755
|
+
entity.dragging = false;
|
|
5756
|
+
entity.pressed = false;
|
|
5757
|
+
entity.pressedAndWithinHitArea = false;
|
|
5758
|
+
this.raiseM2DragEndEvent(entity, m2Event2, domPointerEvent);
|
|
5759
|
+
return;
|
|
5760
|
+
}
|
|
5761
|
+
if (entity.isUserInteractionEnabled && entity.pressed && entity.pressedAndWithinHitArea && !this.IsCanvasPointWithinEntityBounds(
|
|
5762
|
+
entity,
|
|
5763
|
+
domPointerEvent.offsetX,
|
|
5764
|
+
domPointerEvent.offsetY
|
|
5765
|
+
)) {
|
|
5766
|
+
entity.pressedAndWithinHitArea = false;
|
|
5767
|
+
this.raiseTapLeaveEvent(entity, m2Event, domPointerEvent);
|
|
5768
|
+
}
|
|
5769
|
+
if (entity.isUserInteractionEnabled && entity.withinHitArea && !this.IsCanvasPointWithinEntityBounds(
|
|
5770
|
+
entity,
|
|
5771
|
+
domPointerEvent.offsetX,
|
|
5772
|
+
domPointerEvent.offsetY
|
|
5773
|
+
)) {
|
|
5774
|
+
entity.withinHitArea = false;
|
|
5775
|
+
this.raiseM2PointerLeaveEvent(entity, m2Event, domPointerEvent);
|
|
5776
|
+
}
|
|
5777
|
+
if (entity.children) {
|
|
5778
|
+
entity.children.filter((entity2) => !entity2.hidden).filter((entity2) => entity2.isDrawable).sort(
|
|
5779
|
+
(a, b) => b.zPosition - a.zPosition
|
|
5780
|
+
).forEach(
|
|
5781
|
+
(entity2) => this.processDomPointerLeave(entity2, m2Event, domPointerEvent)
|
|
5782
|
+
);
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5641
5785
|
raiseM2PointerDownEvent(entity, m2Event, domPointerEvent) {
|
|
5642
5786
|
m2Event.target = entity;
|
|
5643
5787
|
m2Event.type = EventType.PointerDown;
|
|
@@ -5748,9 +5892,24 @@ class Game {
|
|
|
5748
5892
|
width = radius * 2;
|
|
5749
5893
|
height = radius * 2;
|
|
5750
5894
|
}
|
|
5751
|
-
|
|
5752
|
-
|
|
5895
|
+
let x = domPointerEvent.offsetX;
|
|
5896
|
+
let y = domPointerEvent.offsetY;
|
|
5753
5897
|
const bb = M2c2KitHelpers.calculateEntityAbsoluteBoundingBox(entity);
|
|
5898
|
+
if (M2c2KitHelpers.entityOrAncestorHasBeenRotated(entity)) {
|
|
5899
|
+
const transforms = M2c2KitHelpers.calculateRotationTransforms(
|
|
5900
|
+
entity
|
|
5901
|
+
);
|
|
5902
|
+
transforms.forEach((transform) => {
|
|
5903
|
+
const rotatedPoint = M2c2KitHelpers.rotatePoint(
|
|
5904
|
+
{ x, y },
|
|
5905
|
+
// take negative because we are applying the reverse rotation
|
|
5906
|
+
-transform.radians,
|
|
5907
|
+
transform.center
|
|
5908
|
+
);
|
|
5909
|
+
x = rotatedPoint.x;
|
|
5910
|
+
y = rotatedPoint.y;
|
|
5911
|
+
});
|
|
5912
|
+
}
|
|
5754
5913
|
const relativeX = (x - bb.xMin) / (bb.xMax - bb.xMin) * width;
|
|
5755
5914
|
const relativeY = (y - bb.yMin) / (bb.yMax - bb.yMin) * height;
|
|
5756
5915
|
return { x: relativeX, y: relativeY };
|
|
@@ -7332,7 +7491,7 @@ class Session {
|
|
|
7332
7491
|
this.eventListeners = new Array();
|
|
7333
7492
|
this.sessionDictionary = /* @__PURE__ */ new Map();
|
|
7334
7493
|
this.initialized = false;
|
|
7335
|
-
this.version = "0.3.
|
|
7494
|
+
this.version = "0.3.16 (d8a00e86)";
|
|
7336
7495
|
this.options = options;
|
|
7337
7496
|
for (const activity of this.options.activities) {
|
|
7338
7497
|
if (this.options.activities.filter((a) => a === activity).length > 1) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m2c2kit/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "The m2c2kit core functionality",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@rollup/plugin-commonjs": "25.0.7",
|
|
37
37
|
"@rollup/plugin-node-resolve": "15.2.3",
|
|
38
38
|
"@rollup/plugin-replace": "5.0.5",
|
|
39
|
-
"@types/jest": "29.5.
|
|
39
|
+
"@types/jest": "29.5.11",
|
|
40
40
|
"@types/jsdom": "21.1.6",
|
|
41
41
|
"@webgpu/types": "0.1.40",
|
|
42
42
|
"canvas": "2.11.2",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"jest-environment-jsdom": "29.7.0",
|
|
49
49
|
"jsdom": "23.0.1",
|
|
50
50
|
"rimraf": "5.0.5",
|
|
51
|
-
"rollup": "4.
|
|
51
|
+
"rollup": "4.9.2",
|
|
52
52
|
"rollup-plugin-copy": "3.5.0",
|
|
53
53
|
"rollup-plugin-dts": "6.1.0",
|
|
54
54
|
"rollup-plugin-esbuild": "6.1.0",
|
|
55
55
|
"rollup-plugin-polyfill-node": "0.13.0",
|
|
56
56
|
"ts-jest": "29.1.1",
|
|
57
|
-
"ts-node": "10.9.
|
|
58
|
-
"typescript": "5.3.
|
|
57
|
+
"ts-node": "10.9.2",
|
|
58
|
+
"typescript": "5.3.3"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=18"
|