jspsych-tangram 0.0.11 → 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 +4805 -3935
- 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 +289 -71
- package/dist/construct/index.cjs.map +1 -1
- package/dist/construct/index.d.ts +36 -0
- package/dist/construct/index.js +289 -71
- package/dist/construct/index.js.map +1 -1
- package/dist/index.cjs +399 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +84 -0
- package/dist/index.js +399 -100
- 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 +4803 -3941
- 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 +285 -75
- package/dist/prep/index.cjs.map +1 -1
- package/dist/prep/index.d.ts +24 -0
- package/dist/prep/index.js +285 -75
- 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 +128 -91
- 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/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 },
|
|
@@ -47,9 +57,7 @@ const CONFIG = {
|
|
|
47
57
|
},
|
|
48
58
|
game: {
|
|
49
59
|
snapRadiusPx: 15,
|
|
50
|
-
showBorders: false
|
|
51
|
-
hideTouchingBorders: true
|
|
52
|
-
}
|
|
60
|
+
showBorders: false}
|
|
53
61
|
};
|
|
54
62
|
|
|
55
63
|
function isComposite(bp) {
|
|
@@ -832,6 +840,31 @@ var unlockedIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAA
|
|
|
832
840
|
function pathD(poly) {
|
|
833
841
|
return `M ${poly.map((pt) => `${pt.x} ${pt.y}`).join(" L ")} Z`;
|
|
834
842
|
}
|
|
843
|
+
function getPieceColor(blueprint, usePrimitiveColors, defaultColor, primitiveColorIndices) {
|
|
844
|
+
if (!usePrimitiveColors) {
|
|
845
|
+
return defaultColor;
|
|
846
|
+
}
|
|
847
|
+
if ("kind" in blueprint) {
|
|
848
|
+
const kind = blueprint.kind;
|
|
849
|
+
const kindToIndex = {
|
|
850
|
+
"square": 0,
|
|
851
|
+
"smalltriangle": 1,
|
|
852
|
+
"parallelogram": 2,
|
|
853
|
+
"medtriangle": 3,
|
|
854
|
+
"largetriangle": 4
|
|
855
|
+
};
|
|
856
|
+
const primitiveIndex = kindToIndex[kind];
|
|
857
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
858
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
859
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
860
|
+
if (color) {
|
|
861
|
+
return color;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return defaultColor;
|
|
865
|
+
}
|
|
866
|
+
return defaultColor;
|
|
867
|
+
}
|
|
835
868
|
function BoardView(props) {
|
|
836
869
|
const {
|
|
837
870
|
controller,
|
|
@@ -851,6 +884,9 @@ function BoardView(props) {
|
|
|
851
884
|
dragInvalid,
|
|
852
885
|
lockedPieceId,
|
|
853
886
|
showTangramDecomposition,
|
|
887
|
+
usePrimitiveColorsBlueprints,
|
|
888
|
+
usePrimitiveColorsTargets,
|
|
889
|
+
primitiveColorIndices,
|
|
854
890
|
svgRef,
|
|
855
891
|
setPieceRef,
|
|
856
892
|
onPiecePointerDown,
|
|
@@ -861,6 +897,144 @@ function BoardView(props) {
|
|
|
861
897
|
onCenterBadgePointerDown
|
|
862
898
|
} = props;
|
|
863
899
|
const VW = viewBox.w, VH = viewBox.h;
|
|
900
|
+
const renderSilhouettes = () => layout.sectors.map((s) => {
|
|
901
|
+
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
902
|
+
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
903
|
+
const primitiveDecomposition = sectorCfg.silhouette.primitiveDecomposition;
|
|
904
|
+
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
905
|
+
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
906
|
+
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
907
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
908
|
+
const primInfo = primitiveDecomposition[i];
|
|
909
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
910
|
+
if (usePrimitiveColorsTargets && primInfo?.kind && primitiveColorIndices) {
|
|
911
|
+
const kindToIndex = {
|
|
912
|
+
"square": 0,
|
|
913
|
+
"smalltriangle": 1,
|
|
914
|
+
"parallelogram": 2,
|
|
915
|
+
"medtriangle": 3,
|
|
916
|
+
"largetriangle": 4
|
|
917
|
+
};
|
|
918
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
919
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
920
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
921
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
922
|
+
if (color) {
|
|
923
|
+
fillColor = color;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return /* @__PURE__ */ React.createElement(
|
|
928
|
+
"path",
|
|
929
|
+
{
|
|
930
|
+
key: `prim-fill-${i}`,
|
|
931
|
+
d: pathD(scaledPoly),
|
|
932
|
+
fill: fillColor,
|
|
933
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
934
|
+
stroke: "none"
|
|
935
|
+
}
|
|
936
|
+
);
|
|
937
|
+
}));
|
|
938
|
+
} else {
|
|
939
|
+
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
940
|
+
if (!placedPolys.length) return null;
|
|
941
|
+
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 })));
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
const renderPieces = (piecesFilter) => {
|
|
945
|
+
const piecesToRender = pieces;
|
|
946
|
+
return piecesToRender.sort((a, b) => {
|
|
947
|
+
if (draggingId === a.id) return 1;
|
|
948
|
+
if (draggingId === b.id) return -1;
|
|
949
|
+
return 0;
|
|
950
|
+
}).map((p) => {
|
|
951
|
+
const bp = controller.getBlueprint(p.blueprintId);
|
|
952
|
+
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
953
|
+
const isDragging = draggingId === p.id;
|
|
954
|
+
const locked = p.sectorId && controller.isSectorCompleted(p.sectorId);
|
|
955
|
+
const isConnectivityLocked = lockedPieceId === p.id;
|
|
956
|
+
selectedPieceId === p.id;
|
|
957
|
+
const isCarriedInvalid = isDragging && dragInvalid;
|
|
958
|
+
const translateX = p.x - bb.min.x;
|
|
959
|
+
const translateY = p.y - bb.min.y;
|
|
960
|
+
const validFillColor = getPieceColor(
|
|
961
|
+
bp,
|
|
962
|
+
usePrimitiveColorsBlueprints || false,
|
|
963
|
+
CONFIG.color.piece.validFill,
|
|
964
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
965
|
+
);
|
|
966
|
+
const draggingFillColor = getPieceColor(
|
|
967
|
+
bp,
|
|
968
|
+
usePrimitiveColorsBlueprints || false,
|
|
969
|
+
CONFIG.color.piece.draggingFill,
|
|
970
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
971
|
+
);
|
|
972
|
+
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) => {
|
|
973
|
+
const showBorders = shouldShowBorders();
|
|
974
|
+
shouldUseSelectiveBorders(p.blueprintId);
|
|
975
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: idx }, /* @__PURE__ */ React.createElement(
|
|
976
|
+
"path",
|
|
977
|
+
{
|
|
978
|
+
d: pathD(poly),
|
|
979
|
+
fill: isConnectivityLocked ? CONFIG.color.piece.invalidFill : isCarriedInvalid ? CONFIG.color.piece.invalidFill : isDragging ? draggingFillColor : validFillColor,
|
|
980
|
+
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,
|
|
981
|
+
stroke: "none",
|
|
982
|
+
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
983
|
+
}
|
|
984
|
+
), showBorders);
|
|
985
|
+
}), isDragging && isCarriedInvalid && "shape" in bp && bp.shape.length > 0 && (() => {
|
|
986
|
+
const { cx, cy } = polysAABB$1(bp.shape);
|
|
987
|
+
const size = CONFIG.size.invalidMarker.sizePx;
|
|
988
|
+
const strokeWidth = CONFIG.size.invalidMarker.strokePx;
|
|
989
|
+
const borderWidth = strokeWidth + 2;
|
|
990
|
+
return /* @__PURE__ */ React.createElement("g", { key: "invalid-marker" }, /* @__PURE__ */ React.createElement(
|
|
991
|
+
"line",
|
|
992
|
+
{
|
|
993
|
+
x1: cx - size,
|
|
994
|
+
y1: cy - size,
|
|
995
|
+
x2: cx + size,
|
|
996
|
+
y2: cy + size,
|
|
997
|
+
stroke: "white",
|
|
998
|
+
strokeWidth: borderWidth,
|
|
999
|
+
strokeLinecap: "round"
|
|
1000
|
+
}
|
|
1001
|
+
), /* @__PURE__ */ React.createElement(
|
|
1002
|
+
"line",
|
|
1003
|
+
{
|
|
1004
|
+
x1: cx - size,
|
|
1005
|
+
y1: cy - size,
|
|
1006
|
+
x2: cx + size,
|
|
1007
|
+
y2: cy + size,
|
|
1008
|
+
stroke: CONFIG.color.piece.invalidStroke,
|
|
1009
|
+
strokeWidth,
|
|
1010
|
+
strokeLinecap: "round"
|
|
1011
|
+
}
|
|
1012
|
+
), /* @__PURE__ */ React.createElement(
|
|
1013
|
+
"line",
|
|
1014
|
+
{
|
|
1015
|
+
x1: cx + size,
|
|
1016
|
+
y1: cy - size,
|
|
1017
|
+
x2: cx - size,
|
|
1018
|
+
y2: cy + size,
|
|
1019
|
+
stroke: "white",
|
|
1020
|
+
strokeWidth: borderWidth,
|
|
1021
|
+
strokeLinecap: "round"
|
|
1022
|
+
}
|
|
1023
|
+
), /* @__PURE__ */ React.createElement(
|
|
1024
|
+
"line",
|
|
1025
|
+
{
|
|
1026
|
+
x1: cx + size,
|
|
1027
|
+
y1: cy - size,
|
|
1028
|
+
x2: cx - size,
|
|
1029
|
+
y2: cy + size,
|
|
1030
|
+
stroke: CONFIG.color.piece.invalidStroke,
|
|
1031
|
+
strokeWidth,
|
|
1032
|
+
strokeLinecap: "round"
|
|
1033
|
+
}
|
|
1034
|
+
));
|
|
1035
|
+
})());
|
|
1036
|
+
});
|
|
1037
|
+
};
|
|
864
1038
|
const centerView = controller.state.blueprintView;
|
|
865
1039
|
const bps = centerView === "primitives" ? controller.state.primitives : controller.state.quickstash;
|
|
866
1040
|
const QS_SLOTS = controller.state.cfg.maxQuickstashSlots;
|
|
@@ -881,6 +1055,12 @@ function BoardView(props) {
|
|
|
881
1055
|
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
882
1056
|
const cx = bb.min.x + bb.width / 2;
|
|
883
1057
|
const cy = bb.min.y + bb.height / 2;
|
|
1058
|
+
const fillColor = getPieceColor(
|
|
1059
|
+
bp,
|
|
1060
|
+
usePrimitiveColorsBlueprints || false,
|
|
1061
|
+
CONFIG.color.blueprint.fill,
|
|
1062
|
+
primitiveColorIndices || [0, 1, 2, 3, 4]
|
|
1063
|
+
);
|
|
884
1064
|
return /* @__PURE__ */ React.createElement(
|
|
885
1065
|
"g",
|
|
886
1066
|
{
|
|
@@ -892,7 +1072,7 @@ function BoardView(props) {
|
|
|
892
1072
|
{
|
|
893
1073
|
key: idx,
|
|
894
1074
|
d: pathD(poly),
|
|
895
|
-
fill:
|
|
1075
|
+
fill: fillColor,
|
|
896
1076
|
opacity: CONFIG.opacity.blueprint,
|
|
897
1077
|
stroke: "none",
|
|
898
1078
|
strokeWidth: 0,
|
|
@@ -916,7 +1096,7 @@ function BoardView(props) {
|
|
|
916
1096
|
onPointerDown: (e) => {
|
|
917
1097
|
onRootPointerDown(e);
|
|
918
1098
|
},
|
|
919
|
-
style: { background:
|
|
1099
|
+
style: { background: CONFIG.color.background, touchAction: "none", userSelect: "none" }
|
|
920
1100
|
},
|
|
921
1101
|
layout.sectors.map((s, i) => {
|
|
922
1102
|
const done = !!controller.state.sectors[s.id].completedAt;
|
|
@@ -926,35 +1106,7 @@ function BoardView(props) {
|
|
|
926
1106
|
const work = layout.bands.workspace;
|
|
927
1107
|
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
1108
|
}),
|
|
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
|
-
}),
|
|
1109
|
+
/* @__PURE__ */ React.createElement(React.Fragment, null, renderSilhouettes(), renderPieces()) ,
|
|
958
1110
|
layout.sectors.map((s) => {
|
|
959
1111
|
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
960
1112
|
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
@@ -962,23 +1114,32 @@ function BoardView(props) {
|
|
|
962
1114
|
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
963
1115
|
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
964
1116
|
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
|
|
1117
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
1118
|
+
const primInfo = primitiveDecomposition[i];
|
|
1119
|
+
if (usePrimitiveColorsTargets && primInfo?.kind && primitiveColorIndices) {
|
|
1120
|
+
const kindToIndex = {
|
|
1121
|
+
"square": 0,
|
|
1122
|
+
"smalltriangle": 1,
|
|
1123
|
+
"parallelogram": 2,
|
|
1124
|
+
"medtriangle": 3,
|
|
1125
|
+
"largetriangle": 4
|
|
1126
|
+
};
|
|
1127
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
1128
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
1129
|
+
primitiveColorIndices[primitiveIndex];
|
|
1130
|
+
}
|
|
980
1131
|
}
|
|
981
|
-
|
|
1132
|
+
return /* @__PURE__ */ React.createElement(
|
|
1133
|
+
"path",
|
|
1134
|
+
{
|
|
1135
|
+
key: `prim-fill-${i}`,
|
|
1136
|
+
d: pathD(scaledPoly),
|
|
1137
|
+
fill: "none",
|
|
1138
|
+
opacity: 0,
|
|
1139
|
+
stroke: "none"
|
|
1140
|
+
}
|
|
1141
|
+
);
|
|
1142
|
+
}));
|
|
982
1143
|
} else {
|
|
983
1144
|
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
984
1145
|
if (!placedPolys.length) return null;
|
|
@@ -1069,7 +1230,25 @@ function BoardView(props) {
|
|
|
1069
1230
|
const by = layout.cy + blueprintRingR * Math.sin(theta);
|
|
1070
1231
|
return renderBlueprintGlyph(bp, bx, by);
|
|
1071
1232
|
}),
|
|
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" })))
|
|
1233
|
+
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" }))),
|
|
1234
|
+
showTangramDecomposition && layout.sectors.map((s) => {
|
|
1235
|
+
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
1236
|
+
if (!sectorCfg?.silhouette.primitiveDecomposition) return null;
|
|
1237
|
+
const primitiveDecomposition = sectorCfg.silhouette.primitiveDecomposition;
|
|
1238
|
+
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
1239
|
+
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
1240
|
+
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
1241
|
+
return /* @__PURE__ */ React.createElement("g", { key: `sil-borders-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => /* @__PURE__ */ React.createElement(
|
|
1242
|
+
"path",
|
|
1243
|
+
{
|
|
1244
|
+
key: `prim-border-${i}`,
|
|
1245
|
+
d: pathD(scaledPoly),
|
|
1246
|
+
fill: "none",
|
|
1247
|
+
stroke: CONFIG.color.tangramDecomposition.stroke,
|
|
1248
|
+
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
1249
|
+
}
|
|
1250
|
+
)));
|
|
1251
|
+
})
|
|
1073
1252
|
);
|
|
1074
1253
|
}
|
|
1075
1254
|
|
|
@@ -2954,7 +3133,10 @@ function GameBoard(props) {
|
|
|
2954
3133
|
onPieceRemove,
|
|
2955
3134
|
onInteraction,
|
|
2956
3135
|
onTrialEnd,
|
|
2957
|
-
onControllerReady
|
|
3136
|
+
onControllerReady,
|
|
3137
|
+
usePrimitiveColorsBlueprints,
|
|
3138
|
+
usePrimitiveColorsTargets,
|
|
3139
|
+
primitiveColorIndices
|
|
2958
3140
|
} = props;
|
|
2959
3141
|
const [timeRemaining, setTimeRemaining] = React.useState(timeLimitMs > 0 ? Math.floor(timeLimitMs / 1e3) : 0);
|
|
2960
3142
|
const controller = React.useMemo(() => {
|
|
@@ -3275,19 +3457,28 @@ function GameBoard(props) {
|
|
|
3275
3457
|
const headerStyle = {
|
|
3276
3458
|
display: "flex",
|
|
3277
3459
|
flexDirection: "row",
|
|
3278
|
-
justifyContent: "
|
|
3460
|
+
justifyContent: "center",
|
|
3279
3461
|
alignItems: "center",
|
|
3280
3462
|
padding: "20px",
|
|
3281
|
-
background:
|
|
3463
|
+
background: CONFIG.color.background,
|
|
3282
3464
|
flex: "0 0 auto"
|
|
3283
3465
|
};
|
|
3466
|
+
const headerContentStyle = {
|
|
3467
|
+
position: "relative",
|
|
3468
|
+
width: `${svgDimensions.width}px`,
|
|
3469
|
+
maxWidth: "100%",
|
|
3470
|
+
display: "flex",
|
|
3471
|
+
justifyContent: "center",
|
|
3472
|
+
alignItems: "center"
|
|
3473
|
+
};
|
|
3284
3474
|
const instructionsStyle = {
|
|
3285
|
-
flexGrow: 1,
|
|
3286
3475
|
fontSize: "20px",
|
|
3287
3476
|
lineHeight: 1.5,
|
|
3288
|
-
|
|
3477
|
+
textAlign: "center"
|
|
3289
3478
|
};
|
|
3290
3479
|
const timerStyle = {
|
|
3480
|
+
position: "absolute",
|
|
3481
|
+
right: 0,
|
|
3291
3482
|
fontSize: "24px",
|
|
3292
3483
|
fontWeight: "bold",
|
|
3293
3484
|
fontFamily: "monospace",
|
|
@@ -3302,14 +3493,14 @@ function GameBoard(props) {
|
|
|
3302
3493
|
justifyContent: "center",
|
|
3303
3494
|
overflow: "hidden"
|
|
3304
3495
|
};
|
|
3305
|
-
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(
|
|
3496
|
+
return /* @__PURE__ */ React.createElement("div", { className: "tangram-trial-container", style: containerStyle }, (instructions || timeLimitMs > 0) && /* @__PURE__ */ React.createElement("div", { className: "tangram-header", style: headerStyle }, /* @__PURE__ */ React.createElement("div", { className: "tangram-header-content", style: headerContentStyle }, instructions && /* @__PURE__ */ React.createElement(
|
|
3306
3497
|
"div",
|
|
3307
3498
|
{
|
|
3308
3499
|
className: "tangram-instructions",
|
|
3309
3500
|
style: instructionsStyle,
|
|
3310
3501
|
dangerouslySetInnerHTML: { __html: instructions }
|
|
3311
3502
|
}
|
|
3312
|
-
), 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(
|
|
3503
|
+
), 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(
|
|
3313
3504
|
BoardView,
|
|
3314
3505
|
{
|
|
3315
3506
|
controller,
|
|
@@ -3337,6 +3528,9 @@ function GameBoard(props) {
|
|
|
3337
3528
|
onCenterBadgePointerDown,
|
|
3338
3529
|
showTangramDecomposition: showTangramDecomposition ?? false,
|
|
3339
3530
|
scaleS,
|
|
3531
|
+
usePrimitiveColorsBlueprints: usePrimitiveColorsBlueprints ?? false,
|
|
3532
|
+
usePrimitiveColorsTargets: usePrimitiveColorsTargets ?? false,
|
|
3533
|
+
primitiveColorIndices: primitiveColorIndices ?? [0, 1, 2, 3, 4],
|
|
3340
3534
|
...eventCallbacks
|
|
3341
3535
|
}
|
|
3342
3536
|
))));
|
|
@@ -3556,7 +3750,10 @@ function startConstructionTrial(display_element, params, _jsPsych) {
|
|
|
3556
3750
|
trialParams,
|
|
3557
3751
|
...params.instructions && { instructions: params.instructions },
|
|
3558
3752
|
...params.onInteraction && { onInteraction: params.onInteraction },
|
|
3559
|
-
...params.onTrialEnd && { onTrialEnd: params.onTrialEnd }
|
|
3753
|
+
...params.onTrialEnd && { onTrialEnd: params.onTrialEnd },
|
|
3754
|
+
...params.usePrimitiveColorsBlueprints !== void 0 && { usePrimitiveColorsBlueprints: params.usePrimitiveColorsBlueprints },
|
|
3755
|
+
...params.usePrimitiveColorsTargets !== void 0 && { usePrimitiveColorsTargets: params.usePrimitiveColorsTargets },
|
|
3756
|
+
...params.primitiveColorIndices !== void 0 && { primitiveColorIndices: params.primitiveColorIndices }
|
|
3560
3757
|
};
|
|
3561
3758
|
const root = client.createRoot(display_element);
|
|
3562
3759
|
root.render(React.createElement(GameBoard, gameBoardProps));
|
|
@@ -3641,6 +3838,24 @@ const info$2 = {
|
|
|
3641
3838
|
type: jspsych.ParameterType.FUNCTION,
|
|
3642
3839
|
default: void 0,
|
|
3643
3840
|
description: "Callback when trial completes with full data"
|
|
3841
|
+
},
|
|
3842
|
+
/** Whether to use distinct colors for each primitive shape type in blueprints */
|
|
3843
|
+
use_primitive_colors_blueprints: {
|
|
3844
|
+
type: jspsych.ParameterType.BOOL,
|
|
3845
|
+
default: false,
|
|
3846
|
+
description: "Whether each primitive shape type should have its own distinct color in the blueprint dock area"
|
|
3847
|
+
},
|
|
3848
|
+
/** Whether to use distinct colors for each primitive shape type in target tangrams */
|
|
3849
|
+
use_primitive_colors_targets: {
|
|
3850
|
+
type: jspsych.ParameterType.BOOL,
|
|
3851
|
+
default: false,
|
|
3852
|
+
description: "Whether each primitive shape type should have its own distinct color in target tangram silhouettes"
|
|
3853
|
+
},
|
|
3854
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
3855
|
+
primitive_color_indices: {
|
|
3856
|
+
type: jspsych.ParameterType.OBJECT,
|
|
3857
|
+
default: [0, 1, 2, 3, 4],
|
|
3858
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
3644
3859
|
}
|
|
3645
3860
|
},
|
|
3646
3861
|
data: {
|
|
@@ -3706,7 +3921,10 @@ class TangramConstructPlugin {
|
|
|
3706
3921
|
show_tangram_decomposition: trial.show_tangram_decomposition,
|
|
3707
3922
|
instructions: trial.instructions,
|
|
3708
3923
|
onInteraction: trial.onInteraction,
|
|
3709
|
-
onTrialEnd: wrappedOnTrialEnd
|
|
3924
|
+
onTrialEnd: wrappedOnTrialEnd,
|
|
3925
|
+
usePrimitiveColorsBlueprints: trial.use_primitive_colors_blueprints,
|
|
3926
|
+
usePrimitiveColorsTargets: trial.use_primitive_colors_targets,
|
|
3927
|
+
primitiveColorIndices: trial.primitive_color_indices
|
|
3710
3928
|
};
|
|
3711
3929
|
const { root, display_element: element, jsPsych } = startConstructionTrial(display_element, params, this.jsPsych);
|
|
3712
3930
|
element.__reactContext = { root, jsPsych };
|
|
@@ -3724,7 +3942,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3724
3942
|
quickstashMacros,
|
|
3725
3943
|
primitiveOrder,
|
|
3726
3944
|
onInteraction,
|
|
3727
|
-
onTrialEnd
|
|
3945
|
+
onTrialEnd,
|
|
3946
|
+
usePrimitiveColorsBlueprints,
|
|
3947
|
+
primitiveColorIndices
|
|
3728
3948
|
} = params;
|
|
3729
3949
|
const { onInteraction: _, onTrialEnd: __, ...trialParams } = params;
|
|
3730
3950
|
const PRIMITIVE_BLUEPRINTS_ORDERED = [...PRIMITIVE_BLUEPRINTS].sort((a, b) => primitiveOrder.indexOf(a.kind) - primitiveOrder.indexOf(b.kind));
|
|
@@ -3816,7 +4036,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3816
4036
|
...params.instructions && { instructions: params.instructions },
|
|
3817
4037
|
onControllerReady: handleControllerReady,
|
|
3818
4038
|
...onInteraction && { onInteraction },
|
|
3819
|
-
...onTrialEnd && { onTrialEnd }
|
|
4039
|
+
...onTrialEnd && { onTrialEnd },
|
|
4040
|
+
...usePrimitiveColorsBlueprints !== void 0 && { usePrimitiveColorsBlueprints },
|
|
4041
|
+
...primitiveColorIndices !== void 0 && { primitiveColorIndices }
|
|
3820
4042
|
}));
|
|
3821
4043
|
return { root, display_element, jsPsych };
|
|
3822
4044
|
}
|
|
@@ -3882,6 +4104,18 @@ const info$1 = {
|
|
|
3882
4104
|
onTrialEnd: {
|
|
3883
4105
|
type: jspsych.ParameterType.FUNCTION,
|
|
3884
4106
|
default: void 0
|
|
4107
|
+
},
|
|
4108
|
+
/** Whether to use distinct colors for each primitive shape type in blueprints */
|
|
4109
|
+
use_primitive_colors_blueprints: {
|
|
4110
|
+
type: jspsych.ParameterType.BOOL,
|
|
4111
|
+
default: false,
|
|
4112
|
+
description: "Whether each primitive shape type should have its own distinct color in the blueprint dock area"
|
|
4113
|
+
},
|
|
4114
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
4115
|
+
primitive_color_indices: {
|
|
4116
|
+
type: jspsych.ParameterType.OBJECT,
|
|
4117
|
+
default: [0, 1, 2, 3, 4],
|
|
4118
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
3885
4119
|
}
|
|
3886
4120
|
},
|
|
3887
4121
|
data: {
|
|
@@ -3926,7 +4160,9 @@ class TangramPrepPlugin {
|
|
|
3926
4160
|
primitiveOrder: trial.primitive_order,
|
|
3927
4161
|
instructions: trial.instructions,
|
|
3928
4162
|
onInteraction: trial.onInteraction,
|
|
3929
|
-
onTrialEnd: wrappedOnTrialEnd
|
|
4163
|
+
onTrialEnd: wrappedOnTrialEnd,
|
|
4164
|
+
usePrimitiveColorsBlueprints: trial.use_primitive_colors_blueprints,
|
|
4165
|
+
primitiveColorIndices: trial.primitive_color_indices
|
|
3930
4166
|
};
|
|
3931
4167
|
const { root, display_element: element, jsPsych } = startPrepTrial(display_element, params, this.jsPsych);
|
|
3932
4168
|
element.__reactContext = { root, jsPsych };
|
|
@@ -3946,6 +4182,8 @@ function NBackView({ params }) {
|
|
|
3946
4182
|
instructions,
|
|
3947
4183
|
button_text,
|
|
3948
4184
|
duration,
|
|
4185
|
+
usePrimitiveColors,
|
|
4186
|
+
primitiveColorIndices,
|
|
3949
4187
|
onTrialEnd
|
|
3950
4188
|
} = params;
|
|
3951
4189
|
const trialStartTime = React.useRef(Date.now());
|
|
@@ -4001,7 +4239,12 @@ function NBackView({ params }) {
|
|
|
4001
4239
|
...data,
|
|
4002
4240
|
accuracy,
|
|
4003
4241
|
tangram_id: tangram.tangramID,
|
|
4004
|
-
is_match: isMatch
|
|
4242
|
+
is_match: isMatch,
|
|
4243
|
+
show_tangram_decomposition,
|
|
4244
|
+
use_primitive_colors: usePrimitiveColors,
|
|
4245
|
+
primitive_color_indices: primitiveColorIndices,
|
|
4246
|
+
duration,
|
|
4247
|
+
button_text
|
|
4005
4248
|
};
|
|
4006
4249
|
if (onTrialEnd) {
|
|
4007
4250
|
onTrialEnd(trialData);
|
|
@@ -4055,35 +4298,77 @@ function NBackView({ params }) {
|
|
|
4055
4298
|
if (show_tangram_decomposition) {
|
|
4056
4299
|
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
4057
4300
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, centerPos);
|
|
4058
|
-
return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4301
|
+
return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
4302
|
+
const primInfo = primitiveDecomposition[i];
|
|
4303
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
4304
|
+
if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
|
|
4305
|
+
const kindToIndex = {
|
|
4306
|
+
"square": 0,
|
|
4307
|
+
"smalltriangle": 1,
|
|
4308
|
+
"parallelogram": 2,
|
|
4309
|
+
"medtriangle": 3,
|
|
4310
|
+
"largetriangle": 4
|
|
4311
|
+
};
|
|
4312
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
4313
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
4314
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
4315
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
4316
|
+
if (color) {
|
|
4317
|
+
fillColor = color;
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4073
4320
|
}
|
|
4074
|
-
|
|
4321
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: `prim-${i}` }, /* @__PURE__ */ React.createElement(
|
|
4322
|
+
"path",
|
|
4323
|
+
{
|
|
4324
|
+
d: pathD(scaledPoly),
|
|
4325
|
+
fill: fillColor,
|
|
4326
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
4327
|
+
stroke: "none"
|
|
4328
|
+
}
|
|
4329
|
+
), /* @__PURE__ */ React.createElement(
|
|
4330
|
+
"path",
|
|
4331
|
+
{
|
|
4332
|
+
d: pathD(scaledPoly),
|
|
4333
|
+
fill: "none",
|
|
4334
|
+
stroke: CONFIG.color.tangramDecomposition.stroke,
|
|
4335
|
+
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
4336
|
+
}
|
|
4337
|
+
));
|
|
4338
|
+
}));
|
|
4075
4339
|
} else {
|
|
4076
4340
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(mask, scaleS, centerPos);
|
|
4077
|
-
return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4341
|
+
return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
4342
|
+
const primInfo = primitiveDecomposition[i];
|
|
4343
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
4344
|
+
if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
|
|
4345
|
+
const kindToIndex = {
|
|
4346
|
+
"square": 0,
|
|
4347
|
+
"smalltriangle": 1,
|
|
4348
|
+
"parallelogram": 2,
|
|
4349
|
+
"medtriangle": 3,
|
|
4350
|
+
"largetriangle": 4
|
|
4351
|
+
};
|
|
4352
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
4353
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
4354
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
4355
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
4356
|
+
if (color) {
|
|
4357
|
+
fillColor = color;
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4085
4360
|
}
|
|
4086
|
-
|
|
4361
|
+
return /* @__PURE__ */ React.createElement(
|
|
4362
|
+
"path",
|
|
4363
|
+
{
|
|
4364
|
+
key: `sil-${i}`,
|
|
4365
|
+
d: pathD(scaledPoly),
|
|
4366
|
+
fill: fillColor,
|
|
4367
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
4368
|
+
stroke: "none"
|
|
4369
|
+
}
|
|
4370
|
+
);
|
|
4371
|
+
}));
|
|
4087
4372
|
}
|
|
4088
4373
|
};
|
|
4089
4374
|
return /* @__PURE__ */ React.createElement("div", { style: {
|
|
@@ -4182,6 +4467,18 @@ const info = {
|
|
|
4182
4467
|
default: 3e3,
|
|
4183
4468
|
description: "Duration in milliseconds to display tangram and accept responses"
|
|
4184
4469
|
},
|
|
4470
|
+
/** Whether to use distinct colors for each primitive shape type */
|
|
4471
|
+
use_primitive_colors: {
|
|
4472
|
+
type: jspsych.ParameterType.BOOL,
|
|
4473
|
+
default: false,
|
|
4474
|
+
description: "Whether each primitive shape type should have its own distinct color in the displayed tangram"
|
|
4475
|
+
},
|
|
4476
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
4477
|
+
primitive_color_indices: {
|
|
4478
|
+
type: jspsych.ParameterType.OBJECT,
|
|
4479
|
+
default: [0, 1, 2, 3, 4],
|
|
4480
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
4481
|
+
},
|
|
4185
4482
|
/** Callback fired when trial ends */
|
|
4186
4483
|
onTrialEnd: {
|
|
4187
4484
|
type: jspsych.ParameterType.FUNCTION,
|
|
@@ -4248,6 +4545,8 @@ class TangramNBackPlugin {
|
|
|
4248
4545
|
instructions: trial.instructions,
|
|
4249
4546
|
button_text: trial.button_text,
|
|
4250
4547
|
duration: trial.duration,
|
|
4548
|
+
usePrimitiveColors: trial.use_primitive_colors,
|
|
4549
|
+
primitiveColorIndices: trial.primitive_color_indices,
|
|
4251
4550
|
onTrialEnd: wrappedOnTrialEnd
|
|
4252
4551
|
};
|
|
4253
4552
|
const { root, display_element: element, jsPsych } = startNBackTrial(display_element, params, this.jsPsych);
|