jspsych-tangram 0.0.7 → 0.0.9
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/construct/index.browser.js +187 -214
- package/dist/construct/index.browser.js.map +1 -1
- package/dist/construct/index.browser.min.js +13 -10
- package/dist/construct/index.browser.min.js.map +1 -1
- package/dist/construct/index.cjs +187 -214
- package/dist/construct/index.cjs.map +1 -1
- package/dist/construct/index.d.ts +24 -0
- package/dist/construct/index.js +187 -214
- package/dist/construct/index.js.map +1 -1
- package/dist/index.cjs +198 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +36 -0
- package/dist/index.js +198 -215
- package/dist/index.js.map +1 -1
- package/dist/prep/index.browser.js +173 -213
- package/dist/prep/index.browser.js.map +1 -1
- package/dist/prep/index.browser.min.js +14 -11
- package/dist/prep/index.browser.min.js.map +1 -1
- package/dist/prep/index.cjs +173 -213
- package/dist/prep/index.cjs.map +1 -1
- package/dist/prep/index.d.ts +12 -0
- package/dist/prep/index.js +173 -213
- package/dist/prep/index.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/README.md +6 -0
- package/src/assets/images.d.ts +19 -0
- package/src/assets/locked.png +0 -0
- package/src/assets/unlocked.png +0 -0
- package/src/core/components/board/BoardView.tsx +121 -39
- package/src/core/components/board/GameBoard.tsx +123 -7
- package/src/core/config/config.ts +8 -4
- package/src/core/domain/types.ts +1 -0
- package/src/core/io/InteractionTracker.ts +23 -24
- package/src/core/io/data-tracking.ts +6 -7
- package/src/plugins/tangram-construct/ConstructionApp.tsx +16 -1
- package/src/plugins/tangram-construct/index.ts +14 -0
- package/src/plugins/tangram-prep/PrepApp.tsx +6 -0
- package/src/plugins/tangram-prep/index.ts +7 -0
- package/tangram-construct.min.js +13 -10
- package/tangram-prep.min.js +14 -11
|
@@ -16669,7 +16669,8 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
16669
16669
|
anchors: { invalid: "#7dd3fc", valid: "#475569" },
|
|
16670
16670
|
piece: { draggingFill: "#8e7cc3ff", validFill: "#8e7cc3ff", invalidFill: "#ef4444", invalidStroke: "#dc2626", selectedStroke: "#674ea7", allGreenStroke: "#86efac", borderStroke: "#674ea7" },
|
|
16671
16671
|
ui: { light: "#60a5fa", dark: "#1d4ed8" },
|
|
16672
|
-
blueprint: { fill: "#374151", selectedStroke: "#111827", badgeFill: "#000000", labelFill: "#ffffff" }
|
|
16672
|
+
blueprint: { fill: "#374151", selectedStroke: "#111827", badgeFill: "#000000", labelFill: "#ffffff" },
|
|
16673
|
+
tangramDecomposition: { stroke: "#fef2cc" }
|
|
16673
16674
|
},
|
|
16674
16675
|
opacity: {
|
|
16675
16676
|
blueprint: 0.4,
|
|
@@ -16679,7 +16680,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
16679
16680
|
piece: { invalid: 0.35, dragging: 0.75, locked: 1, normal: 1 }
|
|
16680
16681
|
},
|
|
16681
16682
|
size: {
|
|
16682
|
-
stroke: { bandPx: 5, pieceSelectedPx: 3, allGreenStrokePx: 10, pieceBorderPx: 2 },
|
|
16683
|
+
stroke: { bandPx: 5, pieceSelectedPx: 3, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },
|
|
16683
16684
|
anchorRadiusPx: { valid: 1, invalid: 1 },
|
|
16684
16685
|
badgeFontPx: 16,
|
|
16685
16686
|
centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 }
|
|
@@ -16687,6 +16688,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
16687
16688
|
layout: {
|
|
16688
16689
|
grid: { stepPx: 20, unitPx: 40 },
|
|
16689
16690
|
paddingPx: 1,
|
|
16691
|
+
viewportScale: 0.8,
|
|
16690
16692
|
constraints: {
|
|
16691
16693
|
workspaceDiamAnchors: 10,
|
|
16692
16694
|
// num anchors req'd to be on diagonal
|
|
@@ -16698,7 +16700,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
16698
16700
|
},
|
|
16699
16701
|
game: {
|
|
16700
16702
|
snapRadiusPx: 15,
|
|
16701
|
-
showBorders:
|
|
16703
|
+
showBorders: false,
|
|
16702
16704
|
hideTouchingBorders: true
|
|
16703
16705
|
}
|
|
16704
16706
|
};
|
|
@@ -17469,152 +17471,17 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17469
17471
|
return { cx, cy, w, h };
|
|
17470
17472
|
}
|
|
17471
17473
|
|
|
17472
|
-
function
|
|
17473
|
-
|
|
17474
|
-
for (let i = 0; i < poly.length; i++) {
|
|
17475
|
-
const current = poly[i];
|
|
17476
|
-
const next = poly[(i + 1) % poly.length];
|
|
17477
|
-
if (current && next) {
|
|
17478
|
-
strokePaths.push(`M ${current.x} ${current.y} L ${next.x} ${next.y}`);
|
|
17479
|
-
}
|
|
17480
|
-
}
|
|
17481
|
-
return strokePaths;
|
|
17482
|
-
}
|
|
17483
|
-
function edgeToUnitSegments$1(start, end, gridSize) {
|
|
17484
|
-
const segments = [];
|
|
17485
|
-
const startGrid = {
|
|
17486
|
-
x: Math.round(start.x / gridSize),
|
|
17487
|
-
y: Math.round(start.y / gridSize)
|
|
17488
|
-
};
|
|
17489
|
-
const endGrid = {
|
|
17490
|
-
x: Math.round(end.x / gridSize),
|
|
17491
|
-
y: Math.round(end.y / gridSize)
|
|
17492
|
-
};
|
|
17493
|
-
const dx = endGrid.x - startGrid.x;
|
|
17494
|
-
const dy = endGrid.y - startGrid.y;
|
|
17495
|
-
if (dx === 0 && dy === 0) return [];
|
|
17496
|
-
const steps = Math.max(Math.abs(dx), Math.abs(dy));
|
|
17497
|
-
const stepX = dx / steps;
|
|
17498
|
-
const stepY = dy / steps;
|
|
17499
|
-
for (let i = 0; i < steps; i++) {
|
|
17500
|
-
const aX = Math.round(startGrid.x + i * stepX);
|
|
17501
|
-
const aY = Math.round(startGrid.y + i * stepY);
|
|
17502
|
-
const bX = Math.round(startGrid.x + (i + 1) * stepX);
|
|
17503
|
-
const bY = Math.round(startGrid.y + (i + 1) * stepY);
|
|
17504
|
-
segments.push({
|
|
17505
|
-
a: { x: aX, y: aY },
|
|
17506
|
-
b: { x: bX, y: bY }
|
|
17507
|
-
});
|
|
17508
|
-
}
|
|
17509
|
-
return segments;
|
|
17510
|
-
}
|
|
17511
|
-
function getHiddenEdgesForPolygon(piece, polyIndex, allPiecesInSector, getBlueprint, getPrimitive) {
|
|
17512
|
-
let blueprint;
|
|
17513
|
-
try {
|
|
17514
|
-
blueprint = getBlueprint(piece.blueprintId);
|
|
17515
|
-
} catch (error) {
|
|
17516
|
-
console.warn("getBlueprint failed in getHiddenEdgesForPolygon:", error);
|
|
17517
|
-
return [];
|
|
17518
|
-
}
|
|
17519
|
-
if (!blueprint?.shape) {
|
|
17520
|
-
return [];
|
|
17521
|
-
}
|
|
17522
|
-
const poly = blueprint.shape[polyIndex];
|
|
17523
|
-
if (!poly) return [];
|
|
17524
|
-
const gridSize = CONFIG.layout.grid.stepPx;
|
|
17525
|
-
const bb = boundsOfBlueprint(blueprint, getPrimitive);
|
|
17526
|
-
const ox = piece.pos.x - bb.min.x;
|
|
17527
|
-
const oy = piece.pos.y - bb.min.y;
|
|
17528
|
-
const translatedPoly = poly.map((vertex) => ({
|
|
17529
|
-
x: vertex.x + ox,
|
|
17530
|
-
y: vertex.y + oy
|
|
17531
|
-
}));
|
|
17532
|
-
const hiddenEdges = [];
|
|
17533
|
-
for (let i = 0; i < translatedPoly.length; i++) {
|
|
17534
|
-
const current = translatedPoly[i];
|
|
17535
|
-
const next = translatedPoly[(i + 1) % translatedPoly.length];
|
|
17536
|
-
if (!current || !next) {
|
|
17537
|
-
hiddenEdges.push(false);
|
|
17538
|
-
continue;
|
|
17539
|
-
}
|
|
17540
|
-
const edgeSegments = edgeToUnitSegments$1(current, next, gridSize);
|
|
17541
|
-
let isTouching = false;
|
|
17542
|
-
if (piece.blueprintId.startsWith("comp:")) {
|
|
17543
|
-
for (let otherPolyIndex = 0; otherPolyIndex < blueprint.shape.length; otherPolyIndex++) {
|
|
17544
|
-
if (otherPolyIndex === polyIndex) continue;
|
|
17545
|
-
const otherPoly = blueprint.shape[otherPolyIndex];
|
|
17546
|
-
if (!otherPoly) continue;
|
|
17547
|
-
const otherTranslatedPoly = otherPoly.map((vertex) => ({
|
|
17548
|
-
x: vertex.x + ox,
|
|
17549
|
-
y: vertex.y + oy
|
|
17550
|
-
}));
|
|
17551
|
-
for (let j = 0; j < otherTranslatedPoly.length; j++) {
|
|
17552
|
-
const otherCurrent = otherTranslatedPoly[j];
|
|
17553
|
-
const otherNext = otherTranslatedPoly[(j + 1) % otherTranslatedPoly.length];
|
|
17554
|
-
if (!otherCurrent || !otherNext) continue;
|
|
17555
|
-
const otherEdgeSegments = edgeToUnitSegments$1(otherCurrent, otherNext, gridSize);
|
|
17556
|
-
for (const edgeSeg of edgeSegments) {
|
|
17557
|
-
const isShared = otherEdgeSegments.some(
|
|
17558
|
-
(otherSeg) => (
|
|
17559
|
-
// Check if segments share the same endpoints (identical or reversed)
|
|
17560
|
-
edgeSeg.a.x === otherSeg.a.x && edgeSeg.a.y === otherSeg.a.y && edgeSeg.b.x === otherSeg.b.x && edgeSeg.b.y === otherSeg.b.y || edgeSeg.a.x === otherSeg.b.x && edgeSeg.a.y === otherSeg.b.y && edgeSeg.b.x === otherSeg.a.x && edgeSeg.b.y === otherSeg.a.y
|
|
17561
|
-
)
|
|
17562
|
-
);
|
|
17563
|
-
if (isShared) {
|
|
17564
|
-
isTouching = true;
|
|
17565
|
-
break;
|
|
17566
|
-
}
|
|
17567
|
-
}
|
|
17568
|
-
if (isTouching) break;
|
|
17569
|
-
}
|
|
17570
|
-
if (isTouching) break;
|
|
17571
|
-
}
|
|
17572
|
-
}
|
|
17573
|
-
if (!isTouching && CONFIG.game.hideTouchingBorders) {
|
|
17574
|
-
for (const otherPiece of allPiecesInSector) {
|
|
17575
|
-
if (otherPiece.id === piece.id) continue;
|
|
17576
|
-
const otherBlueprint = getBlueprint(otherPiece.blueprintId);
|
|
17577
|
-
if (!otherBlueprint?.shape) continue;
|
|
17578
|
-
const otherBb = boundsOfBlueprint(otherBlueprint, getPrimitive);
|
|
17579
|
-
const otherOx = otherPiece.pos.x - otherBb.min.x;
|
|
17580
|
-
const otherOy = otherPiece.pos.y - otherBb.min.y;
|
|
17581
|
-
for (const otherPoly of otherBlueprint.shape) {
|
|
17582
|
-
const otherTranslatedPoly = otherPoly.map((vertex) => ({
|
|
17583
|
-
x: vertex.x + otherOx,
|
|
17584
|
-
y: vertex.y + otherOy
|
|
17585
|
-
}));
|
|
17586
|
-
for (let j = 0; j < otherTranslatedPoly.length; j++) {
|
|
17587
|
-
const otherCurrent = otherTranslatedPoly[j];
|
|
17588
|
-
const otherNext = otherTranslatedPoly[(j + 1) % otherTranslatedPoly.length];
|
|
17589
|
-
if (!otherCurrent || !otherNext) continue;
|
|
17590
|
-
const otherEdgeSegments = edgeToUnitSegments$1(otherCurrent, otherNext, gridSize);
|
|
17591
|
-
for (const edgeSeg of edgeSegments) {
|
|
17592
|
-
const isShared = otherEdgeSegments.some(
|
|
17593
|
-
(otherSeg) => (
|
|
17594
|
-
// Check if segments share the same endpoints (identical or reversed)
|
|
17595
|
-
edgeSeg.a.x === otherSeg.a.x && edgeSeg.a.y === otherSeg.a.y && edgeSeg.b.x === otherSeg.b.x && edgeSeg.b.y === otherSeg.b.y || edgeSeg.a.x === otherSeg.b.x && edgeSeg.a.y === otherSeg.b.y && edgeSeg.b.x === otherSeg.a.x && edgeSeg.b.y === otherSeg.a.y
|
|
17596
|
-
)
|
|
17597
|
-
);
|
|
17598
|
-
if (isShared) {
|
|
17599
|
-
isTouching = true;
|
|
17600
|
-
break;
|
|
17601
|
-
}
|
|
17602
|
-
}
|
|
17603
|
-
if (isTouching) break;
|
|
17604
|
-
}
|
|
17605
|
-
if (isTouching) break;
|
|
17606
|
-
}
|
|
17607
|
-
if (isTouching) break;
|
|
17608
|
-
}
|
|
17609
|
-
}
|
|
17610
|
-
hiddenEdges.push(isTouching);
|
|
17611
|
-
}
|
|
17612
|
-
return hiddenEdges;
|
|
17474
|
+
function shouldShowBorders() {
|
|
17475
|
+
return CONFIG.game.showBorders;
|
|
17613
17476
|
}
|
|
17614
17477
|
function shouldUseSelectiveBorders(blueprintId) {
|
|
17615
|
-
return
|
|
17478
|
+
return CONFIG.game.showBorders;
|
|
17616
17479
|
}
|
|
17617
17480
|
|
|
17481
|
+
var lockedIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAACXBIWXMAAAsSAAALEgHS3X78AAAFY0lEQVR4nO2d7XXrNgyGH/r0f7zB9QbXnaDqBt6gvhM0I2SE3A2UDXwnqLKBPEHdDewJ0B+kYieWZEoiQfrjPQcnjqOI0GuYAAFQMiJCLjDGLIEFsHQyB/7w/Pd397MCamAnInVgFUfDpCTaGLMACmDlfj4FHuKAJX4DVCKyC3x+b6gTbYyZA2sn31UHhy1QAqWI7FVHFhEVwVpsCUgmUgKF2vUrEVxlQGyXVBqE3zPBqoTHIHhOXlPEUCmBedZEY6OHfQZkTZU9sMqO6Buw4ujWHYLkJXaBkJqUWFIDy6REYx3eLUwVl2TPREc5heR1BgRoy1qV6DsleRLZD5KVyH6QrET2UMeX+uJyE28H6UvykvuILobKHs/Qz4fkObcdJ0+VGo9FzcV8tDGmBP7qPSg+DtgLqrFWBLYSs8C/AhMTbyKy7j3igjWvSPu1LPH4ajo9y4S6ChdyI5emjFTz8isjcgxYC98kNIxOnfuUTmEhk5e6Tvd1IiMpBxFNmlDOy6kMIDtVpNRqKK3O0BhToetk/sPOxZ0FU1cxX2CNAGxFZCc9lW3XvlARvrreh3cRKc7ezcSaOx0e1tH1hZc1PY6INA79zKrbFKuUlXrpccZDdKnomHqwzlXzmqpeotG35l0PyWMWSa3zPGkiqKKP6FJZmXUH0VVIa0pk1WUr0e5T11REaLe+dYwPEOtIk13fqSLPykp0Wd4uwLm7piTtnM1zM/aMI9boovr6hgvHvgU49zdjTNHyvnZ36bp5MYOPGFW74bDtoouA52871y7g+X3w3XH7YdGFsgJwzMKdYh55zCry+dtQwJHoVQIF7gUrSGvR94ICYOYckGYu4N7wZIxZzLDx5QNxsZxh04kPxMUS9JNINfBChB7kS4KNal4JsygaIhtNooNUTwKS/oxeoqky7kVsbB3JujuhLkCzMKBF9O+S0ebKU7il+j+xx5ldPmQyfuZKMoCIVMCv2ONoEP2qMMZUlLEHiD11HEQkdv4iCIwxUafQ2Bad7ZShjd9SK+CLkz3kTQJsQ4o93RMQNX4MFPN2NcN4t816jBE1ltZwhpPgLLmiPdZ9Aip3TNbInmjsdNG3oHhCvww3GDOOd27JFT5FidwLF+8z2ktKD4TFfkb+Idgm0DEpUV8L0Yeevx94ED0dYttyC9rJPmCzgjtFlcagnjkl+ywmOVxSasHn5M8vYJFzwsrhICK7JryrUmriA7cCPCW1vpJVYQXHODr3Oe6asYEj0VU6PW4eFTii3Ty9TajMrWLbOOrTJXiZRJXbRtm8eBAdF2Xz4oNo58HfUmhzo3g7jYq+Zu9KXV0Go+54nSPKT7+1JMArwiW86wiNL2sm3ERKKfF/VvA42zkbus9BREyoc8WC68r/N+Ap/3RtDB84S/y7A4LlqI0xueeKIWx/+PtXkoHzqeOkRpdV3TCmELbpsbWG2Td4yA2QwefUgCS/BLzO185xehSYB/6ksyObsLeX2zHmxihOkdB3Biixqc3UBC8If6eaom9Mn5tXvQJ/9x40HFtsGKmd5pxjHV/oPZU/ReS57wCvp1YYY2r0N3xeC7YicnF7im9fx4rMqzCJcMCz1cGLaDnW7R74jEI865XenUpia3M/xmp0g/ghQ+qViUOia5XBoWoO8ee1yaj1QC7B/rXIKJK94ug+JLqvXAo0jTqjc+CT2nbdwEtuu7C7xSaKphUaAi1pm62/qb/aoWXUzWiDztEdhD8e4aRB9I1YdzArjkr0CeGNo0xNnK9UBNp4pEr0CeFF5oRXXPODIzsILzMgtpFSg+BGHg/3VcLjcdVKSEr0Vzjil4x/AHvTrF5jm3d2wZUcif8BqSLxz8FiAOgAAAAASUVORK5CYII=";
|
|
17482
|
+
|
|
17483
|
+
var unlockedIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAAA4qEECAAAACXBIWXMAAAsSAAALEgHS3X78AAAFYklEQVR4nO1d0ZGjOBB9ct3/+iJYMlg2giODcwjeDCYEbwbeDNgMvBloIjgmAyYDHEHfh4SHsQUIaLVk7FfVNTblkZpHq1u0GqGICKlAKZUDyADkVrYA/vH891f7VwOoANREVDGrOBsqJtFKqQxAAWBn/35h7uIMQ/wJgCaimrl9b4gTrZTaAthb+SbaOfAGoARQElEj2jMRiQiMxZYAKBEpARRi5y9EsE6A2D7REoQ/MsGihIcgeIu0XMRUKQFskyYaZvbQJEDWUmkA7JIjegVWHNy6OUjOYW4QYpMSSioAeVSiYQLeGlzFmDRYGCiXkLxPgABp2YsS/aAkLyL7SbIQ2U+ShcieGvhin1xq4h0gvbJ3Nk+swZ/G9MEZZooFmKlkDB36cIYhezzv7WHJW8jOkxsAR5i7TOfNAszoOgKoBfXqk6pPz0muA3J3fDXmBBkTN2ITXi4i2lqVhKJHH6sYGXXHyGQP5kbGlJe465tsxSPWHYvoZshYYrsMNpITIbvXhfQpWwgo9eJB2AmfR1Vjjw1eoMhkF1OI1oGV0SNxofZoo8aAX7QXJAbRznOLZc1ZDzlzAtqxp60sEtEEh1XHsGanHwNwWNCm0w0h3mLEjVUnYc1M/d4k55GQVUtbQBUwJpx62q4jEV06iYaZN4fu/MafgtfqXKMlFtGEzrx6gw/sER6u5MuOsf1D94tS6gXAV8b2p2J/+dS58hKJo9tovCwIukTbNrXA+YxJ9cl1QC5ouIiONd+VkqzrOgrEQzI1zIFQALgQzeknh5AJ9ZMSdgDELTpzHDsJ9R0LRfshR4TgkNAUTMRPbyA7nL/ZxymuoQV1iIF8A2PRknDFg7W7j3wD+RnHTX9EdIJZUV4r8r8idPqvUmpLtw/rHBH+oscqV9gCskGhgblrY6+o9xWYmHSAfBWsWEenmAQ7CN9C9q5UpJMyNrEDhGsJDlTLdkC8wyTlZR+g9IR9wLRGYN+9Gf/JYhxSJRkArG7H0P1IWPTfKRMNXIo4/wvaB8IS/U5EWcD22aCUCmpwoV1HHbj9u4GEj2aBUmqnlKqUUmSlUkpJpXdZEHJao5mmYEOFNc4Cmhl9BJ9H65SJhl/NR5E40XoDcyuaMnzcwz60EgvRbJD+mp1PGjcLrcRCVPdAtM+IS31U3gXRPosCqS8cVG0gCJUy5Jp1DBX3ONchEwqGDdFHXYdmuGohUQD44zj+B3FrUnyggY8blqSHHhE1RLQD8LNz+CcR7VLPo8Byey8Wfc/QgCWazA6HbxGVWSveLLefch1lFFXWjbL98CQ6LMr2w4VoG1R+x9BmpfjdDdTXadJSVpdVo+x++UQ0EWl87MP8xHy8Wi4vcCX+DyKqzENbOnZG2nP/w/UB5w40SikN/53Ih8C+ZmgXUkHMu50zrhm+ElFx034P0Zyrwo+2Cv7dZQTONUP7w19MHd/Duh6Xjr96R9pANqut4FmavaqRUM1dz3lyZC8Hz7N3FdwO95cJV7MPXyFQCbQAJ/CUg+0HXaTHFefaq6hEQpYN3mrS0ZV4X6W4nqqtYRZS11Qf7bXw4LvBYGbJ5qy4fId8JRN3xf8ZplK2Hvuh93tYJAoB7xDOqZwL3iVhtsEfs1VaH374kgzAz0df+bg9eHzbPct+Mm8zA8ojkz2Z5NlEPzDZs0j2nnX0IfJ2x5Lw3764B4vqo23HOda9sPsGM4Vbli1kuglIYbfbELJoF2AWH91D+PMVThJEr8S62aw4KNEdwttAGZs4X9FgeFWTONEdwovECde45xdH9hBeJkBsK6UEwa08X+4rhOfrqoUQlehrWOJzzH8BewOTN69gEvI1u5Iz8T+TOcUtjzRRXwAAAABJRU5ErkJggg==";
|
|
17484
|
+
|
|
17618
17485
|
function pathD(poly) {
|
|
17619
17486
|
return `M ${poly.map((pt) => `${pt.x} ${pt.y}`).join(" L ")} Z`;
|
|
17620
17487
|
}
|
|
@@ -17630,11 +17497,13 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17630
17497
|
placedSilBySector,
|
|
17631
17498
|
anchorDots,
|
|
17632
17499
|
pieces,
|
|
17500
|
+
scaleS,
|
|
17633
17501
|
clickMode,
|
|
17634
17502
|
draggingId,
|
|
17635
17503
|
selectedPieceId,
|
|
17636
17504
|
dragInvalid,
|
|
17637
17505
|
lockedPieceId,
|
|
17506
|
+
showTangramDecomposition,
|
|
17638
17507
|
svgRef,
|
|
17639
17508
|
setPieceRef,
|
|
17640
17509
|
onPiecePointerDown,
|
|
@@ -17700,7 +17569,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17700
17569
|
onPointerDown: (e) => {
|
|
17701
17570
|
onRootPointerDown(e);
|
|
17702
17571
|
},
|
|
17703
|
-
style: { background: "#
|
|
17572
|
+
style: { background: "#f5f5f5", touchAction: "none", userSelect: "none" }
|
|
17704
17573
|
},
|
|
17705
17574
|
layout.sectors.map((s, i) => {
|
|
17706
17575
|
const done = !!controller.state.sectors[s.id].completedAt;
|
|
@@ -17720,11 +17589,12 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17720
17589
|
const isDragging = draggingId === p.id;
|
|
17721
17590
|
const locked = p.sectorId && controller.isSectorCompleted(p.sectorId);
|
|
17722
17591
|
const isConnectivityLocked = lockedPieceId === p.id;
|
|
17723
|
-
|
|
17592
|
+
selectedPieceId === p.id;
|
|
17724
17593
|
const isCarriedInvalid = isDragging && dragInvalid;
|
|
17725
17594
|
const translateX = p.x - bb.min.x;
|
|
17726
17595
|
const translateY = p.y - bb.min.y;
|
|
17727
17596
|
return /* @__PURE__ */ React.createElement("g", { key: p.id, ref: setPieceRef(p.id), transform: `translate(${translateX}, ${translateY})`, style: { cursor: locked ? "default" : clickMode ? "pointer" : "grab" }, pointerEvents: clickMode && isDragging ? "none" : "auto" }, ("shape" in bp ? bp.shape : []).map((poly, idx) => {
|
|
17597
|
+
const showBorders = shouldShowBorders();
|
|
17728
17598
|
shouldUseSelectiveBorders(p.blueprintId);
|
|
17729
17599
|
return /* @__PURE__ */ React.createElement(React.Fragment, { key: idx }, /* @__PURE__ */ React.createElement(
|
|
17730
17600
|
"path",
|
|
@@ -17735,51 +17605,38 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17735
17605
|
stroke: "none",
|
|
17736
17606
|
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
17737
17607
|
}
|
|
17738
|
-
),
|
|
17739
|
-
// For pieces with selective borders: render individual edge strokes with edge detection
|
|
17740
|
-
(() => {
|
|
17741
|
-
const allPiecesInSector = pieces.filter((piece) => piece.sectorId === p.sectorId);
|
|
17742
|
-
const pieceAsPiece = { ...p, pos: { x: p.x, y: p.y } };
|
|
17743
|
-
const allPiecesAsPieces = allPiecesInSector.map((piece) => ({ ...piece, pos: { x: piece.x, y: piece.y } }));
|
|
17744
|
-
const hiddenEdges = getHiddenEdgesForPolygon(pieceAsPiece, idx, allPiecesAsPieces, (id) => controller.getBlueprint(id), (kind) => controller.getPrimitive(kind));
|
|
17745
|
-
const draggedPiece = draggingId ? allPiecesInSector.find((piece) => piece.id === draggingId) : null;
|
|
17746
|
-
let wasTouchingDraggedPiece;
|
|
17747
|
-
if (p.blueprintId.startsWith("comp:")) {
|
|
17748
|
-
const internalHiddenEdges = getHiddenEdgesForPolygon(pieceAsPiece, idx, [], (id) => controller.getBlueprint(id), (kind) => controller.getPrimitive(kind));
|
|
17749
|
-
const externalHiddenEdges = draggedPiece ? getHiddenEdgesForPolygon(pieceAsPiece, idx, [{ ...draggedPiece, pos: { x: draggedPiece.x, y: draggedPiece.y } }], (id) => controller.getBlueprint(id), (kind) => controller.getPrimitive(kind)) : new Array(hiddenEdges.length).fill(false);
|
|
17750
|
-
wasTouchingDraggedPiece = externalHiddenEdges.map((external, i) => external && !internalHiddenEdges[i]);
|
|
17751
|
-
} else {
|
|
17752
|
-
wasTouchingDraggedPiece = draggedPiece ? getHiddenEdgesForPolygon(pieceAsPiece, idx, [{ ...draggedPiece, pos: { x: draggedPiece.x, y: draggedPiece.y } }], (id) => controller.getBlueprint(id), (kind) => controller.getPrimitive(kind)) : new Array(hiddenEdges.length).fill(false);
|
|
17753
|
-
}
|
|
17754
|
-
return generateEdgeStrokePaths(poly).map((strokePath, strokeIdx) => {
|
|
17755
|
-
const wasHiddenDueToDraggedPiece = wasTouchingDraggedPiece[strokeIdx] || false;
|
|
17756
|
-
let isHidden;
|
|
17757
|
-
if (isDragging && p.blueprintId.startsWith("comp:")) {
|
|
17758
|
-
const internalHiddenEdges = getHiddenEdgesForPolygon(pieceAsPiece, idx, [], (id) => controller.getBlueprint(id), (kind) => controller.getPrimitive(kind));
|
|
17759
|
-
isHidden = internalHiddenEdges[strokeIdx] || false;
|
|
17760
|
-
} else {
|
|
17761
|
-
isHidden = isDragging ? false : (hiddenEdges[strokeIdx] || false) && !wasHiddenDueToDraggedPiece;
|
|
17762
|
-
}
|
|
17763
|
-
return /* @__PURE__ */ React.createElement(
|
|
17764
|
-
"path",
|
|
17765
|
-
{
|
|
17766
|
-
key: `stroke-${idx}-${strokeIdx}`,
|
|
17767
|
-
d: strokePath,
|
|
17768
|
-
fill: "none",
|
|
17769
|
-
stroke: isHidden ? "none" : isConnectivityLocked ? CONFIG.color.piece.invalidStroke : isCarriedInvalid ? CONFIG.color.piece.invalidStroke : isSelected || isDragging ? CONFIG.color.piece.selectedStroke : CONFIG.color.piece.borderStroke,
|
|
17770
|
-
strokeWidth: isHidden ? 0 : isSelected || isDragging ? CONFIG.size.stroke.pieceSelectedPx : CONFIG.size.stroke.pieceBorderPx,
|
|
17771
|
-
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
17772
|
-
}
|
|
17773
|
-
);
|
|
17774
|
-
});
|
|
17775
|
-
})()
|
|
17776
|
-
) ));
|
|
17608
|
+
), showBorders);
|
|
17777
17609
|
}));
|
|
17778
17610
|
}),
|
|
17779
17611
|
layout.sectors.map((s) => {
|
|
17780
|
-
const
|
|
17781
|
-
if (
|
|
17782
|
-
|
|
17612
|
+
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
17613
|
+
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
17614
|
+
const primitiveDecomposition = sectorCfg.silhouette.primitiveDecomposition;
|
|
17615
|
+
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
17616
|
+
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
17617
|
+
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
17618
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => /* @__PURE__ */ React.createElement(React.Fragment, { key: `prim-${i}` }, /* @__PURE__ */ React.createElement(
|
|
17619
|
+
"path",
|
|
17620
|
+
{
|
|
17621
|
+
d: pathD(scaledPoly),
|
|
17622
|
+
fill: CONFIG.color.silhouetteMask,
|
|
17623
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
17624
|
+
stroke: "none"
|
|
17625
|
+
}
|
|
17626
|
+
), /* @__PURE__ */ React.createElement(
|
|
17627
|
+
"path",
|
|
17628
|
+
{
|
|
17629
|
+
d: pathD(scaledPoly),
|
|
17630
|
+
fill: "none",
|
|
17631
|
+
stroke: CONFIG.color.tangramDecomposition.stroke,
|
|
17632
|
+
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
17633
|
+
}
|
|
17634
|
+
))));
|
|
17635
|
+
} else {
|
|
17636
|
+
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
17637
|
+
if (!placedPolys.length) return null;
|
|
17638
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-${s.id}`, pointerEvents: "none" }, placedPolys.map((poly, i) => /* @__PURE__ */ React.createElement("path", { key: i, d: pathD(poly), fill: CONFIG.color.silhouetteMask, opacity: CONFIG.opacity.silhouetteMask })));
|
|
17639
|
+
}
|
|
17783
17640
|
}),
|
|
17784
17641
|
anchorDots.map(({ sectorId, valid, invalid }) => {
|
|
17785
17642
|
const isInnerRing = sectorId === "inner-ring";
|
|
@@ -17795,10 +17652,20 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17795
17652
|
}
|
|
17796
17653
|
)));
|
|
17797
17654
|
}),
|
|
17655
|
+
/* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("filter", { id: "invert-to-white" }, /* @__PURE__ */ React.createElement(
|
|
17656
|
+
"feColorMatrix",
|
|
17657
|
+
{
|
|
17658
|
+
type: "matrix",
|
|
17659
|
+
values: "-1 0 0 0 1\n 0 -1 0 0 1\n 0 0 -1 0 1\n 0 0 0 1 0"
|
|
17660
|
+
}
|
|
17661
|
+
))),
|
|
17798
17662
|
(() => {
|
|
17799
17663
|
const isPrep = controller.state.cfg.mode === "prep";
|
|
17800
17664
|
const isSubmitEnabled = isPrep ? controller.isSubmitEnabled() : true;
|
|
17801
17665
|
const isClickable = !draggingId && (!isPrep || isSubmitEnabled);
|
|
17666
|
+
const [imageError, setImageError] = React.useState(false);
|
|
17667
|
+
const iconSize = badgeR * 1.6;
|
|
17668
|
+
const iconOffset = iconSize / 2;
|
|
17802
17669
|
return /* @__PURE__ */ React.createElement(
|
|
17803
17670
|
"g",
|
|
17804
17671
|
{
|
|
@@ -17814,7 +17681,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17814
17681
|
opacity: isSubmitEnabled ? 1 : 0.5
|
|
17815
17682
|
}
|
|
17816
17683
|
),
|
|
17817
|
-
/* @__PURE__ */ React.createElement(
|
|
17684
|
+
isPrep ? /* @__PURE__ */ React.createElement(
|
|
17818
17685
|
"text",
|
|
17819
17686
|
{
|
|
17820
17687
|
textAnchor: "middle",
|
|
@@ -17823,7 +17690,29 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
17823
17690
|
fill: isSubmitEnabled ? CONFIG.color.blueprint.labelFill : "#888",
|
|
17824
17691
|
pointerEvents: "none"
|
|
17825
17692
|
},
|
|
17826
|
-
|
|
17693
|
+
"Submit"
|
|
17694
|
+
) : imageError ? /* @__PURE__ */ React.createElement(
|
|
17695
|
+
"text",
|
|
17696
|
+
{
|
|
17697
|
+
textAnchor: "middle",
|
|
17698
|
+
dominantBaseline: "middle",
|
|
17699
|
+
fontSize: CONFIG.size.badgeFontPx,
|
|
17700
|
+
fill: CONFIG.color.blueprint.labelFill,
|
|
17701
|
+
pointerEvents: "none"
|
|
17702
|
+
},
|
|
17703
|
+
"inventory"
|
|
17704
|
+
) : /* @__PURE__ */ React.createElement(
|
|
17705
|
+
"image",
|
|
17706
|
+
{
|
|
17707
|
+
href: controller.state.blueprintView === "quickstash" ? lockedIcon : unlockedIcon,
|
|
17708
|
+
x: -iconOffset,
|
|
17709
|
+
y: -iconOffset,
|
|
17710
|
+
width: iconSize,
|
|
17711
|
+
height: iconSize,
|
|
17712
|
+
pointerEvents: "none",
|
|
17713
|
+
onError: () => setImageError(true),
|
|
17714
|
+
filter: "url(#invert-to-white)"
|
|
17715
|
+
}
|
|
17827
17716
|
)
|
|
17828
17717
|
);
|
|
17829
17718
|
})(),
|
|
@@ -19297,7 +19186,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19297
19186
|
}
|
|
19298
19187
|
|
|
19299
19188
|
class InteractionTracker {
|
|
19300
|
-
constructor(controller, callbacks,
|
|
19189
|
+
constructor(controller, callbacks, trialParams) {
|
|
19301
19190
|
this.gridStep = CONFIG.layout.grid.stepPx;
|
|
19302
19191
|
// Interaction state
|
|
19303
19192
|
this.interactionIndex = 0;
|
|
@@ -19314,8 +19203,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19314
19203
|
this.createdMacros = [];
|
|
19315
19204
|
this.controller = controller;
|
|
19316
19205
|
this.callbacks = callbacks;
|
|
19317
|
-
this.
|
|
19318
|
-
this.gameId = gameId || v4();
|
|
19206
|
+
this.trialParams = trialParams;
|
|
19319
19207
|
this.trialStartTime = Date.now();
|
|
19320
19208
|
this.controller.setTrackingCallbacks({
|
|
19321
19209
|
onSectorCompleted: (sectorId) => this.recordSectorCompletion(sectorId)
|
|
@@ -19363,8 +19251,6 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19363
19251
|
const event = {
|
|
19364
19252
|
// Metadata
|
|
19365
19253
|
interactionId: v4(),
|
|
19366
|
-
trialId: this.trialId,
|
|
19367
|
-
gameId: this.gameId,
|
|
19368
19254
|
interactionIndex: this.interactionIndex++,
|
|
19369
19255
|
// Interaction type
|
|
19370
19256
|
interactionType,
|
|
@@ -19470,6 +19356,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19470
19356
|
const trialEndTime = Date.now();
|
|
19471
19357
|
const totalDuration = trialEndTime - this.trialStartTime;
|
|
19472
19358
|
const finalSnapshot = this.buildStateSnapshot();
|
|
19359
|
+
const anchorToStimuliRatio = CONFIG.layout.grid.stepPx / CONFIG.layout.grid.unitPx;
|
|
19473
19360
|
const mode = this.controller.state.cfg.mode;
|
|
19474
19361
|
if (mode === "construction") {
|
|
19475
19362
|
const finalBlueprintState = this.buildFinalBlueprintState(finalSnapshot);
|
|
@@ -19481,13 +19368,11 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19481
19368
|
}));
|
|
19482
19369
|
const data = {
|
|
19483
19370
|
trialType: "construction",
|
|
19484
|
-
trialId: this.trialId,
|
|
19485
|
-
gameId: this.gameId,
|
|
19486
|
-
trialNum: 0,
|
|
19487
|
-
// TODO: Plugin should provide this
|
|
19488
19371
|
trialStartTime: this.trialStartTime,
|
|
19489
19372
|
trialEndTime,
|
|
19490
19373
|
totalDuration,
|
|
19374
|
+
anchorToStimuliRatio,
|
|
19375
|
+
trialParams: this.trialParams,
|
|
19491
19376
|
endReason,
|
|
19492
19377
|
completionTimes: this.completionTimes,
|
|
19493
19378
|
finalBlueprintState,
|
|
@@ -19506,13 +19391,11 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19506
19391
|
}));
|
|
19507
19392
|
const data = {
|
|
19508
19393
|
trialType: "prep",
|
|
19509
|
-
trialId: this.trialId,
|
|
19510
|
-
gameId: this.gameId,
|
|
19511
|
-
trialNum: 0,
|
|
19512
|
-
// TODO: Plugin should provide this
|
|
19513
19394
|
trialStartTime: this.trialStartTime,
|
|
19514
19395
|
trialEndTime,
|
|
19515
19396
|
totalDuration,
|
|
19397
|
+
anchorToStimuliRatio,
|
|
19398
|
+
trialParams: this.trialParams,
|
|
19516
19399
|
endReason: "submit",
|
|
19517
19400
|
createdMacros: finalMacros,
|
|
19518
19401
|
quickstashMacros,
|
|
@@ -19770,6 +19653,9 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19770
19653
|
maxQuickstashSlots,
|
|
19771
19654
|
maxCompositeSize,
|
|
19772
19655
|
mode,
|
|
19656
|
+
showTangramDecomposition,
|
|
19657
|
+
instructions,
|
|
19658
|
+
trialParams,
|
|
19773
19659
|
width: _width,
|
|
19774
19660
|
height: _height,
|
|
19775
19661
|
onSectorComplete,
|
|
@@ -19779,6 +19665,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19779
19665
|
onTrialEnd,
|
|
19780
19666
|
onControllerReady
|
|
19781
19667
|
} = props;
|
|
19668
|
+
const [timeRemaining, setTimeRemaining] = React.useState(timeLimitMs > 0 ? Math.floor(timeLimitMs / 1e3) : 0);
|
|
19782
19669
|
const controller = React.useMemo(() => {
|
|
19783
19670
|
const gameConfig = {
|
|
19784
19671
|
n: sectors.length,
|
|
@@ -19805,8 +19692,8 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19805
19692
|
const callbacks = {};
|
|
19806
19693
|
if (onInteraction) callbacks.onInteraction = onInteraction;
|
|
19807
19694
|
if (onTrialEnd) callbacks.onTrialEnd = onTrialEnd;
|
|
19808
|
-
return new InteractionTracker(controller, callbacks);
|
|
19809
|
-
}, [controller, onInteraction, onTrialEnd]);
|
|
19695
|
+
return new InteractionTracker(controller, callbacks, trialParams);
|
|
19696
|
+
}, [controller, onInteraction, onTrialEnd, trialParams]);
|
|
19810
19697
|
React.useEffect(() => {
|
|
19811
19698
|
if (onControllerReady) {
|
|
19812
19699
|
onControllerReady(controller);
|
|
@@ -19886,22 +19773,22 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
19886
19773
|
}), [handleSectorComplete, onPiecePlace, onPieceRemove]);
|
|
19887
19774
|
const getGameboardStyle = () => {
|
|
19888
19775
|
const baseStyle = {
|
|
19889
|
-
margin: "
|
|
19776
|
+
margin: "10px",
|
|
19890
19777
|
display: "flex",
|
|
19891
19778
|
alignItems: "center",
|
|
19892
19779
|
justifyContent: "center",
|
|
19893
19780
|
position: "relative"
|
|
19894
19781
|
};
|
|
19895
19782
|
if (layoutMode === "circle") {
|
|
19896
|
-
const size = Math.min(window.innerWidth *
|
|
19783
|
+
const size = Math.min(window.innerWidth * CONFIG.layout.viewportScale, window.innerHeight * CONFIG.layout.viewportScale);
|
|
19897
19784
|
return {
|
|
19898
19785
|
...baseStyle,
|
|
19899
19786
|
width: `${size}px`,
|
|
19900
19787
|
height: `${size}px`
|
|
19901
19788
|
};
|
|
19902
19789
|
} else {
|
|
19903
|
-
const maxWidth = Math.min(window.innerWidth *
|
|
19904
|
-
const maxHeight = Math.min(window.innerWidth *
|
|
19790
|
+
const maxWidth = Math.min(window.innerWidth * CONFIG.layout.viewportScale, window.innerHeight * CONFIG.layout.viewportScale * 2);
|
|
19791
|
+
const maxHeight = Math.min(window.innerWidth * CONFIG.layout.viewportScale / 2, window.innerHeight * CONFIG.layout.viewportScale);
|
|
19905
19792
|
return {
|
|
19906
19793
|
...baseStyle,
|
|
19907
19794
|
width: `${maxWidth}px`,
|
|
@@ -20070,7 +19957,68 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20070
19957
|
force();
|
|
20071
19958
|
e.stopPropagation();
|
|
20072
19959
|
};
|
|
20073
|
-
|
|
19960
|
+
React.useEffect(() => {
|
|
19961
|
+
if (timeLimitMs === 0) return;
|
|
19962
|
+
const interval = setInterval(() => {
|
|
19963
|
+
setTimeRemaining((prev) => {
|
|
19964
|
+
if (prev <= 1) {
|
|
19965
|
+
clearInterval(interval);
|
|
19966
|
+
return 0;
|
|
19967
|
+
}
|
|
19968
|
+
return prev - 1;
|
|
19969
|
+
});
|
|
19970
|
+
}, 1e3);
|
|
19971
|
+
return () => clearInterval(interval);
|
|
19972
|
+
}, [timeLimitMs]);
|
|
19973
|
+
const formatTime = (seconds) => {
|
|
19974
|
+
const mins = Math.floor(seconds / 60);
|
|
19975
|
+
const secs = Math.floor(seconds % 60);
|
|
19976
|
+
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
19977
|
+
};
|
|
19978
|
+
const containerStyle = {
|
|
19979
|
+
display: "flex",
|
|
19980
|
+
flexDirection: "column",
|
|
19981
|
+
width: "100%"
|
|
19982
|
+
// minHeight: '100vh'
|
|
19983
|
+
};
|
|
19984
|
+
const headerStyle = {
|
|
19985
|
+
display: "flex",
|
|
19986
|
+
flexDirection: "row",
|
|
19987
|
+
justifyContent: "space-between",
|
|
19988
|
+
alignItems: "center",
|
|
19989
|
+
padding: "20px",
|
|
19990
|
+
background: "#f5f5f5",
|
|
19991
|
+
flex: "0 0 auto"
|
|
19992
|
+
};
|
|
19993
|
+
const instructionsStyle = {
|
|
19994
|
+
flexGrow: 1,
|
|
19995
|
+
fontSize: "20px",
|
|
19996
|
+
lineHeight: 1.5,
|
|
19997
|
+
marginRight: "20px"
|
|
19998
|
+
};
|
|
19999
|
+
const timerStyle = {
|
|
20000
|
+
fontSize: "24px",
|
|
20001
|
+
fontWeight: "bold",
|
|
20002
|
+
fontFamily: "monospace",
|
|
20003
|
+
color: "#333",
|
|
20004
|
+
minWidth: "80px",
|
|
20005
|
+
textAlign: "right"
|
|
20006
|
+
};
|
|
20007
|
+
const gameboardWrapperStyle = {
|
|
20008
|
+
flex: "1 1 auto",
|
|
20009
|
+
display: "flex",
|
|
20010
|
+
alignItems: "center",
|
|
20011
|
+
justifyContent: "center",
|
|
20012
|
+
overflow: "hidden"
|
|
20013
|
+
};
|
|
20014
|
+
return /* @__PURE__ */ React.createElement("div", { className: "tangram-trial-container", style: containerStyle }, (instructions || timeLimitMs > 0) && /* @__PURE__ */ React.createElement("div", { className: "tangram-header", style: headerStyle }, instructions && /* @__PURE__ */ React.createElement(
|
|
20015
|
+
"div",
|
|
20016
|
+
{
|
|
20017
|
+
className: "tangram-instructions",
|
|
20018
|
+
style: instructionsStyle,
|
|
20019
|
+
dangerouslySetInnerHTML: { __html: instructions }
|
|
20020
|
+
}
|
|
20021
|
+
), timeLimitMs > 0 && /* @__PURE__ */ React.createElement("div", { className: "tangram-timer", style: timerStyle }, formatTime(timeRemaining))), /* @__PURE__ */ React.createElement("div", { className: "tangram-gameboard-wrapper", style: gameboardWrapperStyle }, /* @__PURE__ */ React.createElement("div", { className: "tangram-gameboard", style: getGameboardStyle() }, /* @__PURE__ */ React.createElement(
|
|
20074
20022
|
BoardView,
|
|
20075
20023
|
{
|
|
20076
20024
|
controller,
|
|
@@ -20096,9 +20044,11 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20096
20044
|
onPointerMove,
|
|
20097
20045
|
onPointerUp,
|
|
20098
20046
|
onCenterBadgePointerDown,
|
|
20047
|
+
showTangramDecomposition: showTangramDecomposition ?? false,
|
|
20048
|
+
scaleS,
|
|
20099
20049
|
...eventCallbacks
|
|
20100
20050
|
}
|
|
20101
|
-
));
|
|
20051
|
+
))));
|
|
20102
20052
|
}
|
|
20103
20053
|
|
|
20104
20054
|
const U = 40;
|
|
@@ -20266,17 +20216,22 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20266
20216
|
const isCanonical = CANON.has(tanName);
|
|
20267
20217
|
return isCanonical;
|
|
20268
20218
|
});
|
|
20269
|
-
const mask = filteredTans.map((tan
|
|
20219
|
+
const mask = filteredTans.map((tan) => {
|
|
20270
20220
|
const polygon = tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }));
|
|
20271
20221
|
return polygon;
|
|
20272
20222
|
});
|
|
20223
|
+
const primitiveDecomposition = filteredTans.map((tan) => ({
|
|
20224
|
+
kind: tan.name ?? tan.kind,
|
|
20225
|
+
polygon: tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }))
|
|
20226
|
+
}));
|
|
20273
20227
|
const sectorId = `sector${index}`;
|
|
20274
20228
|
const sector = {
|
|
20275
20229
|
id: sectorId,
|
|
20276
20230
|
tangramId: tangramSpec.tangramID,
|
|
20277
20231
|
silhouette: {
|
|
20278
20232
|
id: sectorId,
|
|
20279
|
-
mask
|
|
20233
|
+
mask,
|
|
20234
|
+
primitiveDecomposition
|
|
20280
20235
|
}
|
|
20281
20236
|
};
|
|
20282
20237
|
return sector;
|
|
@@ -20295,6 +20250,7 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20295
20250
|
quickstash = params.quickstash_macros;
|
|
20296
20251
|
}
|
|
20297
20252
|
}
|
|
20253
|
+
const { onInteraction, onTrialEnd, ...trialParams } = params;
|
|
20298
20254
|
const gameBoardProps = {
|
|
20299
20255
|
sectors,
|
|
20300
20256
|
quickstash,
|
|
@@ -20305,6 +20261,9 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20305
20261
|
timeLimitMs: params.time_limit_ms,
|
|
20306
20262
|
maxQuickstashSlots: CONFIG.layout.defaults.maxQuickstashSlots,
|
|
20307
20263
|
mode: "construction",
|
|
20264
|
+
showTangramDecomposition: params.show_tangram_decomposition ?? false,
|
|
20265
|
+
trialParams,
|
|
20266
|
+
...params.instructions && { instructions: params.instructions },
|
|
20308
20267
|
...params.onInteraction && { onInteraction: params.onInteraction },
|
|
20309
20268
|
...params.onTrialEnd && { onTrialEnd: params.onTrialEnd }
|
|
20310
20269
|
};
|
|
@@ -20368,6 +20327,18 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20368
20327
|
default: 18,
|
|
20369
20328
|
description: "Snap radius for anchor-based piece placement"
|
|
20370
20329
|
},
|
|
20330
|
+
/** Whether to show tangram target shapes decomposed into individual primitives with borders */
|
|
20331
|
+
show_tangram_decomposition: {
|
|
20332
|
+
type: jspsych.ParameterType.BOOL,
|
|
20333
|
+
default: false,
|
|
20334
|
+
description: "Whether to show tangram target shapes decomposed into individual primitives with borders"
|
|
20335
|
+
},
|
|
20336
|
+
/** HTML content to display above the gameboard as instructions */
|
|
20337
|
+
instructions: {
|
|
20338
|
+
type: jspsych.ParameterType.STRING,
|
|
20339
|
+
default: "",
|
|
20340
|
+
description: "HTML content to display above the gameboard as instructions"
|
|
20341
|
+
},
|
|
20371
20342
|
/** Callback fired after each interaction (piece pickup + placedown) */
|
|
20372
20343
|
onInteraction: {
|
|
20373
20344
|
type: jspsych.ParameterType.FUNCTION,
|
|
@@ -20441,6 +20412,8 @@ var TangramConstructPlugin = (function (jspsych) {
|
|
|
20441
20412
|
input: trial.input,
|
|
20442
20413
|
layout: trial.layout,
|
|
20443
20414
|
time_limit_ms: trial.time_limit_ms,
|
|
20415
|
+
show_tangram_decomposition: trial.show_tangram_decomposition,
|
|
20416
|
+
instructions: trial.instructions,
|
|
20444
20417
|
onInteraction: trial.onInteraction,
|
|
20445
20418
|
onTrialEnd: wrappedOnTrialEnd
|
|
20446
20419
|
};
|