jspsych-tangram 0.0.12 → 0.0.13
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 +4809 -3948
- package/dist/construct/index.browser.js.map +1 -1
- package/dist/construct/index.browser.min.js +13 -13
- package/dist/construct/index.browser.min.js.map +1 -1
- package/dist/construct/index.cjs +275 -66
- package/dist/construct/index.cjs.map +1 -1
- package/dist/construct/index.d.ts +36 -0
- package/dist/construct/index.js +275 -66
- package/dist/construct/index.js.map +1 -1
- package/dist/index.cjs +385 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +84 -0
- package/dist/index.js +385 -95
- package/dist/index.js.map +1 -1
- package/dist/nback/index.browser.js +4629 -3939
- package/dist/nback/index.browser.js.map +1 -1
- package/dist/nback/index.browser.min.js +12 -12
- package/dist/nback/index.browser.min.js.map +1 -1
- package/dist/nback/index.cjs +102 -64
- package/dist/nback/index.cjs.map +1 -1
- package/dist/nback/index.d.ts +24 -0
- package/dist/nback/index.js +102 -64
- package/dist/nback/index.js.map +1 -1
- package/dist/prep/index.browser.js +4805 -3952
- package/dist/prep/index.browser.js.map +1 -1
- package/dist/prep/index.browser.min.js +13 -13
- package/dist/prep/index.browser.min.js.map +1 -1
- package/dist/prep/index.cjs +271 -70
- package/dist/prep/index.cjs.map +1 -1
- package/dist/prep/index.d.ts +24 -0
- package/dist/prep/index.js +271 -70
- package/dist/prep/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/components/board/BoardView.tsx +372 -124
- package/src/core/components/board/GameBoard.tsx +26 -2
- package/src/core/components/pieces/BlueprintRing.tsx +105 -47
- package/src/core/config/config.ts +25 -10
- package/src/plugins/tangram-construct/ConstructionApp.tsx +7 -1
- package/src/plugins/tangram-construct/index.ts +22 -1
- package/src/plugins/tangram-nback/NBackApp.tsx +87 -28
- package/src/plugins/tangram-nback/index.ts +14 -0
- package/src/plugins/tangram-prep/PrepApp.tsx +7 -1
- package/src/plugins/tangram-prep/index.ts +14 -0
- package/tangram-construct.min.js +13 -13
- package/tangram-nback.min.js +12 -12
- package/tangram-prep.min.js +13 -13
package/dist/prep/index.cjs
CHANGED
|
@@ -7,30 +7,40 @@ var uuid = require('uuid');
|
|
|
7
7
|
|
|
8
8
|
const CONFIG = {
|
|
9
9
|
color: {
|
|
10
|
+
background: "#fff7e0ff",
|
|
10
11
|
bands: {
|
|
11
|
-
silhouette: { fillEven: "#
|
|
12
|
-
workspace: { fillEven: "#
|
|
12
|
+
silhouette: { fillEven: "#ffffff", fillOdd: "#ffffff", stroke: "#b1b1b1" },
|
|
13
|
+
workspace: { fillEven: "#ffffff", fillOdd: "#ffffff", stroke: "#b1b1b1" }
|
|
13
14
|
},
|
|
14
|
-
completion: { fill: "#
|
|
15
|
+
completion: { fill: "#ccffcc", stroke: "#13da57" },
|
|
15
16
|
silhouetteMask: "#374151",
|
|
16
17
|
anchors: { invalid: "#7dd3fc", valid: "#475569" },
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
blueprint: { fill: "#374151",
|
|
20
|
-
tangramDecomposition: { stroke: "#fef2cc" }
|
|
18
|
+
// validFill used here for placed composites
|
|
19
|
+
piece: { draggingFill: "#8e7cc3", validFill: "#8e7cc3", invalidFill: "#d55c00", invalidStroke: "#dc2626", allGreenStroke: "#86efac"},
|
|
20
|
+
blueprint: { fill: "#374151", badgeFill: "#000000", labelFill: "#ffffff" },
|
|
21
|
+
tangramDecomposition: { stroke: "#fef2cc" },
|
|
22
|
+
primitiveColors: [
|
|
23
|
+
// from seaborn "colorblind" palette, 6 colors, with red omitted
|
|
24
|
+
"#0173b2",
|
|
25
|
+
"#de8f05",
|
|
26
|
+
"#029e73",
|
|
27
|
+
"#cc78bc",
|
|
28
|
+
"#ca9161"
|
|
29
|
+
]
|
|
21
30
|
},
|
|
22
31
|
opacity: {
|
|
23
|
-
blueprint: 0.
|
|
32
|
+
blueprint: 0.6,
|
|
24
33
|
silhouetteMask: 0.25,
|
|
25
34
|
//anchors: { valid: 0.80, invalid: 0.50 },
|
|
26
35
|
anchors: { invalid: 0, valid: 0 },
|
|
27
|
-
piece: { invalid:
|
|
36
|
+
piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 }
|
|
28
37
|
},
|
|
29
38
|
size: {
|
|
30
|
-
stroke: { bandPx: 5,
|
|
39
|
+
stroke: { bandPx: 5, allGreenStrokePx: 10, tangramDecompositionPx: 1 },
|
|
31
40
|
anchorRadiusPx: { valid: 1, invalid: 1 },
|
|
32
41
|
badgeFontPx: 16,
|
|
33
|
-
centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 }
|
|
42
|
+
centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },
|
|
43
|
+
invalidMarker: { sizePx: 10, strokePx: 4 }
|
|
34
44
|
},
|
|
35
45
|
layout: {
|
|
36
46
|
grid: { stepPx: 20, unitPx: 40 },
|
|
@@ -42,14 +52,10 @@ const CONFIG = {
|
|
|
42
52
|
quickstashDiamAnchors: 7,
|
|
43
53
|
// num anchors req'd to be in single quickstash slot
|
|
44
54
|
primitiveDiamAnchors: 5
|
|
45
|
-
},
|
|
46
|
-
defaults: { maxQuickstashSlots: 1 }
|
|
47
|
-
},
|
|
55
|
+
}},
|
|
48
56
|
game: {
|
|
49
57
|
snapRadiusPx: 15,
|
|
50
|
-
showBorders: false
|
|
51
|
-
hideTouchingBorders: true
|
|
52
|
-
}
|
|
58
|
+
showBorders: false}
|
|
53
59
|
};
|
|
54
60
|
|
|
55
61
|
function isComposite(bp) {
|
|
@@ -832,6 +838,31 @@ var unlockedIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAA
|
|
|
832
838
|
function pathD(poly) {
|
|
833
839
|
return `M ${poly.map((pt) => `${pt.x} ${pt.y}`).join(" L ")} Z`;
|
|
834
840
|
}
|
|
841
|
+
function getPieceColor(blueprint, usePrimitiveColors, defaultColor, primitiveColorIndices) {
|
|
842
|
+
if (!usePrimitiveColors) {
|
|
843
|
+
return defaultColor;
|
|
844
|
+
}
|
|
845
|
+
if ("kind" in blueprint) {
|
|
846
|
+
const kind = blueprint.kind;
|
|
847
|
+
const kindToIndex = {
|
|
848
|
+
"square": 0,
|
|
849
|
+
"smalltriangle": 1,
|
|
850
|
+
"parallelogram": 2,
|
|
851
|
+
"medtriangle": 3,
|
|
852
|
+
"largetriangle": 4
|
|
853
|
+
};
|
|
854
|
+
const primitiveIndex = kindToIndex[kind];
|
|
855
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
856
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
857
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
858
|
+
if (color) {
|
|
859
|
+
return color;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return defaultColor;
|
|
863
|
+
}
|
|
864
|
+
return defaultColor;
|
|
865
|
+
}
|
|
835
866
|
function BoardView(props) {
|
|
836
867
|
const {
|
|
837
868
|
controller,
|
|
@@ -851,6 +882,9 @@ function BoardView(props) {
|
|
|
851
882
|
dragInvalid,
|
|
852
883
|
lockedPieceId,
|
|
853
884
|
showTangramDecomposition,
|
|
885
|
+
usePrimitiveColorsBlueprints,
|
|
886
|
+
usePrimitiveColorsTargets,
|
|
887
|
+
primitiveColorIndices,
|
|
854
888
|
svgRef,
|
|
855
889
|
setPieceRef,
|
|
856
890
|
onPiecePointerDown,
|
|
@@ -861,6 +895,144 @@ function BoardView(props) {
|
|
|
861
895
|
onCenterBadgePointerDown
|
|
862
896
|
} = props;
|
|
863
897
|
const VW = viewBox.w, VH = viewBox.h;
|
|
898
|
+
const renderSilhouettes = () => layout.sectors.map((s) => {
|
|
899
|
+
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
900
|
+
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
901
|
+
const primitiveDecomposition = sectorCfg.silhouette.primitiveDecomposition;
|
|
902
|
+
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
903
|
+
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
904
|
+
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
905
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
906
|
+
const primInfo = primitiveDecomposition[i];
|
|
907
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
908
|
+
if (usePrimitiveColorsTargets && primInfo?.kind && primitiveColorIndices) {
|
|
909
|
+
const kindToIndex = {
|
|
910
|
+
"square": 0,
|
|
911
|
+
"smalltriangle": 1,
|
|
912
|
+
"parallelogram": 2,
|
|
913
|
+
"medtriangle": 3,
|
|
914
|
+
"largetriangle": 4
|
|
915
|
+
};
|
|
916
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
917
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
918
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
919
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
920
|
+
if (color) {
|
|
921
|
+
fillColor = color;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return /* @__PURE__ */ React.createElement(
|
|
926
|
+
"path",
|
|
927
|
+
{
|
|
928
|
+
key: `prim-fill-${i}`,
|
|
929
|
+
d: pathD(scaledPoly),
|
|
930
|
+
fill: fillColor,
|
|
931
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
932
|
+
stroke: "none"
|
|
933
|
+
}
|
|
934
|
+
);
|
|
935
|
+
}));
|
|
936
|
+
} else {
|
|
937
|
+
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
938
|
+
if (!placedPolys.length) return null;
|
|
939
|
+
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 })));
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
const renderPieces = (piecesFilter) => {
|
|
943
|
+
const piecesToRender = pieces;
|
|
944
|
+
return piecesToRender.sort((a, b) => {
|
|
945
|
+
if (draggingId === a.id) return 1;
|
|
946
|
+
if (draggingId === b.id) return -1;
|
|
947
|
+
return 0;
|
|
948
|
+
}).map((p) => {
|
|
949
|
+
const bp = controller.getBlueprint(p.blueprintId);
|
|
950
|
+
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
951
|
+
const isDragging = draggingId === p.id;
|
|
952
|
+
const locked = p.sectorId && controller.isSectorCompleted(p.sectorId);
|
|
953
|
+
const isConnectivityLocked = lockedPieceId === p.id;
|
|
954
|
+
selectedPieceId === p.id;
|
|
955
|
+
const isCarriedInvalid = isDragging && dragInvalid;
|
|
956
|
+
const translateX = p.x - bb.min.x;
|
|
957
|
+
const translateY = p.y - bb.min.y;
|
|
958
|
+
const validFillColor = getPieceColor(
|
|
959
|
+
bp,
|
|
960
|
+
usePrimitiveColorsBlueprints || false,
|
|
961
|
+
CONFIG.color.piece.validFill,
|
|
962
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
963
|
+
);
|
|
964
|
+
const draggingFillColor = getPieceColor(
|
|
965
|
+
bp,
|
|
966
|
+
usePrimitiveColorsBlueprints || false,
|
|
967
|
+
CONFIG.color.piece.draggingFill,
|
|
968
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
969
|
+
);
|
|
970
|
+
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) => {
|
|
971
|
+
const showBorders = shouldShowBorders();
|
|
972
|
+
shouldUseSelectiveBorders(p.blueprintId);
|
|
973
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: idx }, /* @__PURE__ */ React.createElement(
|
|
974
|
+
"path",
|
|
975
|
+
{
|
|
976
|
+
d: pathD(poly),
|
|
977
|
+
fill: isConnectivityLocked ? CONFIG.color.piece.invalidFill : isCarriedInvalid ? CONFIG.color.piece.invalidFill : isDragging ? draggingFillColor : validFillColor,
|
|
978
|
+
opacity: isConnectivityLocked ? CONFIG.opacity.piece.invalid : isCarriedInvalid ? CONFIG.opacity.piece.invalid : isDragging ? CONFIG.opacity.piece.dragging : locked ? CONFIG.opacity.piece.locked : CONFIG.opacity.piece.normal,
|
|
979
|
+
stroke: "none",
|
|
980
|
+
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
981
|
+
}
|
|
982
|
+
), showBorders);
|
|
983
|
+
}), isDragging && isCarriedInvalid && "shape" in bp && bp.shape.length > 0 && (() => {
|
|
984
|
+
const { cx, cy } = polysAABB$1(bp.shape);
|
|
985
|
+
const size = CONFIG.size.invalidMarker.sizePx;
|
|
986
|
+
const strokeWidth = CONFIG.size.invalidMarker.strokePx;
|
|
987
|
+
const borderWidth = strokeWidth + 2;
|
|
988
|
+
return /* @__PURE__ */ React.createElement("g", { key: "invalid-marker" }, /* @__PURE__ */ React.createElement(
|
|
989
|
+
"line",
|
|
990
|
+
{
|
|
991
|
+
x1: cx - size,
|
|
992
|
+
y1: cy - size,
|
|
993
|
+
x2: cx + size,
|
|
994
|
+
y2: cy + size,
|
|
995
|
+
stroke: "white",
|
|
996
|
+
strokeWidth: borderWidth,
|
|
997
|
+
strokeLinecap: "round"
|
|
998
|
+
}
|
|
999
|
+
), /* @__PURE__ */ React.createElement(
|
|
1000
|
+
"line",
|
|
1001
|
+
{
|
|
1002
|
+
x1: cx - size,
|
|
1003
|
+
y1: cy - size,
|
|
1004
|
+
x2: cx + size,
|
|
1005
|
+
y2: cy + size,
|
|
1006
|
+
stroke: CONFIG.color.piece.invalidStroke,
|
|
1007
|
+
strokeWidth,
|
|
1008
|
+
strokeLinecap: "round"
|
|
1009
|
+
}
|
|
1010
|
+
), /* @__PURE__ */ React.createElement(
|
|
1011
|
+
"line",
|
|
1012
|
+
{
|
|
1013
|
+
x1: cx + size,
|
|
1014
|
+
y1: cy - size,
|
|
1015
|
+
x2: cx - size,
|
|
1016
|
+
y2: cy + size,
|
|
1017
|
+
stroke: "white",
|
|
1018
|
+
strokeWidth: borderWidth,
|
|
1019
|
+
strokeLinecap: "round"
|
|
1020
|
+
}
|
|
1021
|
+
), /* @__PURE__ */ React.createElement(
|
|
1022
|
+
"line",
|
|
1023
|
+
{
|
|
1024
|
+
x1: cx + size,
|
|
1025
|
+
y1: cy - size,
|
|
1026
|
+
x2: cx - size,
|
|
1027
|
+
y2: cy + size,
|
|
1028
|
+
stroke: CONFIG.color.piece.invalidStroke,
|
|
1029
|
+
strokeWidth,
|
|
1030
|
+
strokeLinecap: "round"
|
|
1031
|
+
}
|
|
1032
|
+
));
|
|
1033
|
+
})());
|
|
1034
|
+
});
|
|
1035
|
+
};
|
|
864
1036
|
const centerView = controller.state.blueprintView;
|
|
865
1037
|
const bps = centerView === "primitives" ? controller.state.primitives : controller.state.quickstash;
|
|
866
1038
|
const QS_SLOTS = controller.state.cfg.maxQuickstashSlots;
|
|
@@ -881,6 +1053,12 @@ function BoardView(props) {
|
|
|
881
1053
|
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
882
1054
|
const cx = bb.min.x + bb.width / 2;
|
|
883
1055
|
const cy = bb.min.y + bb.height / 2;
|
|
1056
|
+
const fillColor = getPieceColor(
|
|
1057
|
+
bp,
|
|
1058
|
+
usePrimitiveColorsBlueprints || false,
|
|
1059
|
+
CONFIG.color.blueprint.fill,
|
|
1060
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
1061
|
+
);
|
|
884
1062
|
return /* @__PURE__ */ React.createElement(
|
|
885
1063
|
"g",
|
|
886
1064
|
{
|
|
@@ -892,7 +1070,7 @@ function BoardView(props) {
|
|
|
892
1070
|
{
|
|
893
1071
|
key: idx,
|
|
894
1072
|
d: pathD(poly),
|
|
895
|
-
fill:
|
|
1073
|
+
fill: fillColor,
|
|
896
1074
|
opacity: CONFIG.opacity.blueprint,
|
|
897
1075
|
stroke: "none",
|
|
898
1076
|
strokeWidth: 0,
|
|
@@ -916,7 +1094,7 @@ function BoardView(props) {
|
|
|
916
1094
|
onPointerDown: (e) => {
|
|
917
1095
|
onRootPointerDown(e);
|
|
918
1096
|
},
|
|
919
|
-
style: { background:
|
|
1097
|
+
style: { background: CONFIG.color.background, touchAction: "none", userSelect: "none" }
|
|
920
1098
|
},
|
|
921
1099
|
layout.sectors.map((s, i) => {
|
|
922
1100
|
const done = !!controller.state.sectors[s.id].completedAt;
|
|
@@ -926,35 +1104,7 @@ function BoardView(props) {
|
|
|
926
1104
|
const work = layout.bands.workspace;
|
|
927
1105
|
return /* @__PURE__ */ React.createElement("g", { key: `bands-${s.id}` }, controller.state.cfg.target === "workspace" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("path", { d: wedgePath(layout.cx, layout.cy, sil[0], sil[1], s.start, s.end), fill: baseSil, stroke: CONFIG.color.bands.silhouette.stroke, strokeWidth: CONFIG.size.stroke.bandPx, pointerEvents: "none" }), /* @__PURE__ */ React.createElement("path", { d: wedgePath(layout.cx, layout.cy, work[0], work[1], s.start, s.end), fill: done ? CONFIG.color.completion.fill : baseWork, stroke: done ? CONFIG.color.completion.stroke : CONFIG.color.bands.workspace.stroke, strokeWidth: CONFIG.size.stroke.bandPx, pointerEvents: "none" })) : /* @__PURE__ */ React.createElement("path", { d: wedgePath(layout.cx, layout.cy, sil[0], sil[1], s.start, s.end), fill: done ? CONFIG.color.completion.fill : baseSil, stroke: done ? CONFIG.color.completion.stroke : CONFIG.color.bands.silhouette.stroke, strokeWidth: CONFIG.size.stroke.bandPx, pointerEvents: "none" }));
|
|
928
1106
|
}),
|
|
929
|
-
|
|
930
|
-
if (draggingId === a.id) return 1;
|
|
931
|
-
if (draggingId === b.id) return -1;
|
|
932
|
-
return 0;
|
|
933
|
-
}).map((p) => {
|
|
934
|
-
const bp = controller.getBlueprint(p.blueprintId);
|
|
935
|
-
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
936
|
-
const isDragging = draggingId === p.id;
|
|
937
|
-
const locked = p.sectorId && controller.isSectorCompleted(p.sectorId);
|
|
938
|
-
const isConnectivityLocked = lockedPieceId === p.id;
|
|
939
|
-
selectedPieceId === p.id;
|
|
940
|
-
const isCarriedInvalid = isDragging && dragInvalid;
|
|
941
|
-
const translateX = p.x - bb.min.x;
|
|
942
|
-
const translateY = p.y - bb.min.y;
|
|
943
|
-
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) => {
|
|
944
|
-
const showBorders = shouldShowBorders();
|
|
945
|
-
shouldUseSelectiveBorders(p.blueprintId);
|
|
946
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: idx }, /* @__PURE__ */ React.createElement(
|
|
947
|
-
"path",
|
|
948
|
-
{
|
|
949
|
-
d: pathD(poly),
|
|
950
|
-
fill: isConnectivityLocked ? CONFIG.color.piece.invalidFill : isCarriedInvalid ? CONFIG.color.piece.invalidFill : isDragging ? CONFIG.color.piece.draggingFill : CONFIG.color.piece.validFill,
|
|
951
|
-
opacity: isConnectivityLocked ? CONFIG.opacity.piece.invalid : isCarriedInvalid ? CONFIG.opacity.piece.invalid : isDragging ? CONFIG.opacity.piece.dragging : locked ? CONFIG.opacity.piece.locked : CONFIG.opacity.piece.normal,
|
|
952
|
-
stroke: "none",
|
|
953
|
-
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
954
|
-
}
|
|
955
|
-
), showBorders);
|
|
956
|
-
}));
|
|
957
|
-
}),
|
|
1107
|
+
/* @__PURE__ */ React.createElement(React.Fragment, null, renderSilhouettes(), renderPieces()) ,
|
|
958
1108
|
layout.sectors.map((s) => {
|
|
959
1109
|
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
960
1110
|
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
@@ -962,23 +1112,32 @@ function BoardView(props) {
|
|
|
962
1112
|
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
963
1113
|
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
964
1114
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
965
|
-
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
966
|
-
|
|
967
|
-
{
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
1115
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
1116
|
+
const primInfo = primitiveDecomposition[i];
|
|
1117
|
+
if (usePrimitiveColorsTargets && primInfo?.kind && primitiveColorIndices) {
|
|
1118
|
+
const kindToIndex = {
|
|
1119
|
+
"square": 0,
|
|
1120
|
+
"smalltriangle": 1,
|
|
1121
|
+
"parallelogram": 2,
|
|
1122
|
+
"medtriangle": 3,
|
|
1123
|
+
"largetriangle": 4
|
|
1124
|
+
};
|
|
1125
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
1126
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
1127
|
+
primitiveColorIndices[primitiveIndex];
|
|
1128
|
+
}
|
|
980
1129
|
}
|
|
981
|
-
|
|
1130
|
+
return /* @__PURE__ */ React.createElement(
|
|
1131
|
+
"path",
|
|
1132
|
+
{
|
|
1133
|
+
key: `prim-fill-${i}`,
|
|
1134
|
+
d: pathD(scaledPoly),
|
|
1135
|
+
fill: "none",
|
|
1136
|
+
opacity: 0,
|
|
1137
|
+
stroke: "none"
|
|
1138
|
+
}
|
|
1139
|
+
);
|
|
1140
|
+
}));
|
|
982
1141
|
} else {
|
|
983
1142
|
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
984
1143
|
if (!placedPolys.length) return null;
|
|
@@ -1069,7 +1228,25 @@ function BoardView(props) {
|
|
|
1069
1228
|
const by = layout.cy + blueprintRingR * Math.sin(theta);
|
|
1070
1229
|
return renderBlueprintGlyph(bp, bx, by);
|
|
1071
1230
|
}),
|
|
1072
|
-
controller.state.endedAt && /* @__PURE__ */ React.createElement("g", { pointerEvents: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: layout.cx, cy: layout.cy, r: layout.outerR - 3, fill: "none", stroke: CONFIG.color.piece.allGreenStroke, strokeWidth: CONFIG.size.stroke.allGreenStrokePx, opacity: 0 }, /* @__PURE__ */ React.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "160ms", fill: "freeze" }), /* @__PURE__ */ React.createElement("animate", { attributeName: "opacity", from: "1", to: "0", begin: "560ms", dur: "520ms", fill: "freeze" })))
|
|
1231
|
+
controller.state.endedAt && /* @__PURE__ */ React.createElement("g", { pointerEvents: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: layout.cx, cy: layout.cy, r: layout.outerR - 3, fill: "none", stroke: CONFIG.color.piece.allGreenStroke, strokeWidth: CONFIG.size.stroke.allGreenStrokePx, opacity: 0 }, /* @__PURE__ */ React.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "160ms", fill: "freeze" }), /* @__PURE__ */ React.createElement("animate", { attributeName: "opacity", from: "1", to: "0", begin: "560ms", dur: "520ms", fill: "freeze" }))),
|
|
1232
|
+
showTangramDecomposition && layout.sectors.map((s) => {
|
|
1233
|
+
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
1234
|
+
if (!sectorCfg?.silhouette.primitiveDecomposition) return null;
|
|
1235
|
+
const primitiveDecomposition = sectorCfg.silhouette.primitiveDecomposition;
|
|
1236
|
+
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
1237
|
+
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
1238
|
+
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
1239
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-borders-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => /* @__PURE__ */ React.createElement(
|
|
1240
|
+
"path",
|
|
1241
|
+
{
|
|
1242
|
+
key: `prim-border-${i}`,
|
|
1243
|
+
d: pathD(scaledPoly),
|
|
1244
|
+
fill: "none",
|
|
1245
|
+
stroke: CONFIG.color.tangramDecomposition.stroke,
|
|
1246
|
+
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
1247
|
+
}
|
|
1248
|
+
)));
|
|
1249
|
+
})
|
|
1073
1250
|
);
|
|
1074
1251
|
}
|
|
1075
1252
|
|
|
@@ -2954,7 +3131,10 @@ function GameBoard(props) {
|
|
|
2954
3131
|
onPieceRemove,
|
|
2955
3132
|
onInteraction,
|
|
2956
3133
|
onTrialEnd,
|
|
2957
|
-
onControllerReady
|
|
3134
|
+
onControllerReady,
|
|
3135
|
+
usePrimitiveColorsBlueprints,
|
|
3136
|
+
usePrimitiveColorsTargets,
|
|
3137
|
+
primitiveColorIndices
|
|
2958
3138
|
} = props;
|
|
2959
3139
|
const [timeRemaining, setTimeRemaining] = React.useState(timeLimitMs > 0 ? Math.floor(timeLimitMs / 1e3) : 0);
|
|
2960
3140
|
const controller = React.useMemo(() => {
|
|
@@ -3278,7 +3458,7 @@ function GameBoard(props) {
|
|
|
3278
3458
|
justifyContent: "center",
|
|
3279
3459
|
alignItems: "center",
|
|
3280
3460
|
padding: "20px",
|
|
3281
|
-
background:
|
|
3461
|
+
background: CONFIG.color.background,
|
|
3282
3462
|
flex: "0 0 auto"
|
|
3283
3463
|
};
|
|
3284
3464
|
const headerContentStyle = {
|
|
@@ -3346,6 +3526,9 @@ function GameBoard(props) {
|
|
|
3346
3526
|
onCenterBadgePointerDown,
|
|
3347
3527
|
showTangramDecomposition: showTangramDecomposition ?? false,
|
|
3348
3528
|
scaleS,
|
|
3529
|
+
usePrimitiveColorsBlueprints: usePrimitiveColorsBlueprints ?? false,
|
|
3530
|
+
usePrimitiveColorsTargets: usePrimitiveColorsTargets ?? false,
|
|
3531
|
+
primitiveColorIndices: primitiveColorIndices ?? [0, 1, 2, 3, 4],
|
|
3349
3532
|
...eventCallbacks
|
|
3350
3533
|
}
|
|
3351
3534
|
))));
|
|
@@ -3512,7 +3695,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3512
3695
|
quickstashMacros,
|
|
3513
3696
|
primitiveOrder,
|
|
3514
3697
|
onInteraction,
|
|
3515
|
-
onTrialEnd
|
|
3698
|
+
onTrialEnd,
|
|
3699
|
+
usePrimitiveColorsBlueprints,
|
|
3700
|
+
primitiveColorIndices
|
|
3516
3701
|
} = params;
|
|
3517
3702
|
const { onInteraction: _, onTrialEnd: __, ...trialParams } = params;
|
|
3518
3703
|
const PRIMITIVE_BLUEPRINTS_ORDERED = [...PRIMITIVE_BLUEPRINTS].sort((a, b) => primitiveOrder.indexOf(a.kind) - primitiveOrder.indexOf(b.kind));
|
|
@@ -3604,7 +3789,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3604
3789
|
...params.instructions && { instructions: params.instructions },
|
|
3605
3790
|
onControllerReady: handleControllerReady,
|
|
3606
3791
|
...onInteraction && { onInteraction },
|
|
3607
|
-
...onTrialEnd && { onTrialEnd }
|
|
3792
|
+
...onTrialEnd && { onTrialEnd },
|
|
3793
|
+
...usePrimitiveColorsBlueprints !== void 0 && { usePrimitiveColorsBlueprints },
|
|
3794
|
+
...primitiveColorIndices !== void 0 && { primitiveColorIndices }
|
|
3608
3795
|
}));
|
|
3609
3796
|
return { root, display_element, jsPsych };
|
|
3610
3797
|
}
|
|
@@ -3670,6 +3857,18 @@ const info = {
|
|
|
3670
3857
|
onTrialEnd: {
|
|
3671
3858
|
type: jspsych.ParameterType.FUNCTION,
|
|
3672
3859
|
default: void 0
|
|
3860
|
+
},
|
|
3861
|
+
/** Whether to use distinct colors for each primitive shape type in blueprints */
|
|
3862
|
+
use_primitive_colors_blueprints: {
|
|
3863
|
+
type: jspsych.ParameterType.BOOL,
|
|
3864
|
+
default: false,
|
|
3865
|
+
description: "Whether each primitive shape type should have its own distinct color in the blueprint dock area"
|
|
3866
|
+
},
|
|
3867
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
3868
|
+
primitive_color_indices: {
|
|
3869
|
+
type: jspsych.ParameterType.OBJECT,
|
|
3870
|
+
default: [0, 1, 2, 3, 4],
|
|
3871
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
3673
3872
|
}
|
|
3674
3873
|
},
|
|
3675
3874
|
data: {
|
|
@@ -3714,7 +3913,9 @@ class TangramPrepPlugin {
|
|
|
3714
3913
|
primitiveOrder: trial.primitive_order,
|
|
3715
3914
|
instructions: trial.instructions,
|
|
3716
3915
|
onInteraction: trial.onInteraction,
|
|
3717
|
-
onTrialEnd: wrappedOnTrialEnd
|
|
3916
|
+
onTrialEnd: wrappedOnTrialEnd,
|
|
3917
|
+
usePrimitiveColorsBlueprints: trial.use_primitive_colors_blueprints,
|
|
3918
|
+
primitiveColorIndices: trial.primitive_color_indices
|
|
3718
3919
|
};
|
|
3719
3920
|
const { root, display_element: element, jsPsych } = startPrepTrial(display_element, params, this.jsPsych);
|
|
3720
3921
|
element.__reactContext = { root, jsPsych };
|