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.js
CHANGED
|
@@ -5,30 +5,40 @@ import { v4 } from 'uuid';
|
|
|
5
5
|
|
|
6
6
|
const CONFIG = {
|
|
7
7
|
color: {
|
|
8
|
+
background: "#fff7e0ff",
|
|
8
9
|
bands: {
|
|
9
|
-
silhouette: { fillEven: "#
|
|
10
|
-
workspace: { fillEven: "#
|
|
10
|
+
silhouette: { fillEven: "#ffffff", fillOdd: "#ffffff", stroke: "#b1b1b1" },
|
|
11
|
+
workspace: { fillEven: "#ffffff", fillOdd: "#ffffff", stroke: "#b1b1b1" }
|
|
11
12
|
},
|
|
12
|
-
completion: { fill: "#
|
|
13
|
+
completion: { fill: "#ccffcc", stroke: "#13da57" },
|
|
13
14
|
silhouetteMask: "#374151",
|
|
14
15
|
anchors: { invalid: "#7dd3fc", valid: "#475569" },
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
blueprint: { fill: "#374151",
|
|
18
|
-
tangramDecomposition: { stroke: "#fef2cc" }
|
|
16
|
+
// validFill used here for placed composites
|
|
17
|
+
piece: { draggingFill: "#8e7cc3", validFill: "#8e7cc3", invalidFill: "#d55c00", invalidStroke: "#dc2626", allGreenStroke: "#86efac"},
|
|
18
|
+
blueprint: { fill: "#374151", badgeFill: "#000000", labelFill: "#ffffff" },
|
|
19
|
+
tangramDecomposition: { stroke: "#fef2cc" },
|
|
20
|
+
primitiveColors: [
|
|
21
|
+
// from seaborn "colorblind" palette, 6 colors, with red omitted
|
|
22
|
+
"#0173b2",
|
|
23
|
+
"#de8f05",
|
|
24
|
+
"#029e73",
|
|
25
|
+
"#cc78bc",
|
|
26
|
+
"#ca9161"
|
|
27
|
+
]
|
|
19
28
|
},
|
|
20
29
|
opacity: {
|
|
21
|
-
blueprint: 0.
|
|
30
|
+
blueprint: 0.6,
|
|
22
31
|
silhouetteMask: 0.25,
|
|
23
32
|
//anchors: { valid: 0.80, invalid: 0.50 },
|
|
24
33
|
anchors: { invalid: 0, valid: 0 },
|
|
25
|
-
piece: { invalid:
|
|
34
|
+
piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 }
|
|
26
35
|
},
|
|
27
36
|
size: {
|
|
28
|
-
stroke: { bandPx: 5,
|
|
37
|
+
stroke: { bandPx: 5, allGreenStrokePx: 10, tangramDecompositionPx: 1 },
|
|
29
38
|
anchorRadiusPx: { valid: 1, invalid: 1 },
|
|
30
39
|
badgeFontPx: 16,
|
|
31
|
-
centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 }
|
|
40
|
+
centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },
|
|
41
|
+
invalidMarker: { sizePx: 10, strokePx: 4 }
|
|
32
42
|
},
|
|
33
43
|
layout: {
|
|
34
44
|
grid: { stepPx: 20, unitPx: 40 },
|
|
@@ -45,9 +55,7 @@ const CONFIG = {
|
|
|
45
55
|
},
|
|
46
56
|
game: {
|
|
47
57
|
snapRadiusPx: 15,
|
|
48
|
-
showBorders: false
|
|
49
|
-
hideTouchingBorders: true
|
|
50
|
-
}
|
|
58
|
+
showBorders: false}
|
|
51
59
|
};
|
|
52
60
|
|
|
53
61
|
function isComposite(bp) {
|
|
@@ -830,6 +838,31 @@ var unlockedIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAYAAA
|
|
|
830
838
|
function pathD(poly) {
|
|
831
839
|
return `M ${poly.map((pt) => `${pt.x} ${pt.y}`).join(" L ")} Z`;
|
|
832
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
|
+
}
|
|
833
866
|
function BoardView(props) {
|
|
834
867
|
const {
|
|
835
868
|
controller,
|
|
@@ -849,6 +882,9 @@ function BoardView(props) {
|
|
|
849
882
|
dragInvalid,
|
|
850
883
|
lockedPieceId,
|
|
851
884
|
showTangramDecomposition,
|
|
885
|
+
usePrimitiveColorsBlueprints,
|
|
886
|
+
usePrimitiveColorsTargets,
|
|
887
|
+
primitiveColorIndices,
|
|
852
888
|
svgRef,
|
|
853
889
|
setPieceRef,
|
|
854
890
|
onPiecePointerDown,
|
|
@@ -859,6 +895,144 @@ function BoardView(props) {
|
|
|
859
895
|
onCenterBadgePointerDown
|
|
860
896
|
} = props;
|
|
861
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
|
+
};
|
|
862
1036
|
const centerView = controller.state.blueprintView;
|
|
863
1037
|
const bps = centerView === "primitives" ? controller.state.primitives : controller.state.quickstash;
|
|
864
1038
|
const QS_SLOTS = controller.state.cfg.maxQuickstashSlots;
|
|
@@ -879,6 +1053,12 @@ function BoardView(props) {
|
|
|
879
1053
|
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
880
1054
|
const cx = bb.min.x + bb.width / 2;
|
|
881
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
|
+
);
|
|
882
1062
|
return /* @__PURE__ */ React.createElement(
|
|
883
1063
|
"g",
|
|
884
1064
|
{
|
|
@@ -890,7 +1070,7 @@ function BoardView(props) {
|
|
|
890
1070
|
{
|
|
891
1071
|
key: idx,
|
|
892
1072
|
d: pathD(poly),
|
|
893
|
-
fill:
|
|
1073
|
+
fill: fillColor,
|
|
894
1074
|
opacity: CONFIG.opacity.blueprint,
|
|
895
1075
|
stroke: "none",
|
|
896
1076
|
strokeWidth: 0,
|
|
@@ -914,7 +1094,7 @@ function BoardView(props) {
|
|
|
914
1094
|
onPointerDown: (e) => {
|
|
915
1095
|
onRootPointerDown(e);
|
|
916
1096
|
},
|
|
917
|
-
style: { background:
|
|
1097
|
+
style: { background: CONFIG.color.background, touchAction: "none", userSelect: "none" }
|
|
918
1098
|
},
|
|
919
1099
|
layout.sectors.map((s, i) => {
|
|
920
1100
|
const done = !!controller.state.sectors[s.id].completedAt;
|
|
@@ -924,35 +1104,7 @@ function BoardView(props) {
|
|
|
924
1104
|
const work = layout.bands.workspace;
|
|
925
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" }));
|
|
926
1106
|
}),
|
|
927
|
-
|
|
928
|
-
if (draggingId === a.id) return 1;
|
|
929
|
-
if (draggingId === b.id) return -1;
|
|
930
|
-
return 0;
|
|
931
|
-
}).map((p) => {
|
|
932
|
-
const bp = controller.getBlueprint(p.blueprintId);
|
|
933
|
-
const bb = boundsOfBlueprint(bp, (k) => controller.getPrimitive(k));
|
|
934
|
-
const isDragging = draggingId === p.id;
|
|
935
|
-
const locked = p.sectorId && controller.isSectorCompleted(p.sectorId);
|
|
936
|
-
const isConnectivityLocked = lockedPieceId === p.id;
|
|
937
|
-
selectedPieceId === p.id;
|
|
938
|
-
const isCarriedInvalid = isDragging && dragInvalid;
|
|
939
|
-
const translateX = p.x - bb.min.x;
|
|
940
|
-
const translateY = p.y - bb.min.y;
|
|
941
|
-
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) => {
|
|
942
|
-
const showBorders = shouldShowBorders();
|
|
943
|
-
shouldUseSelectiveBorders(p.blueprintId);
|
|
944
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, { key: idx }, /* @__PURE__ */ React.createElement(
|
|
945
|
-
"path",
|
|
946
|
-
{
|
|
947
|
-
d: pathD(poly),
|
|
948
|
-
fill: isConnectivityLocked ? CONFIG.color.piece.invalidFill : isCarriedInvalid ? CONFIG.color.piece.invalidFill : isDragging ? CONFIG.color.piece.draggingFill : CONFIG.color.piece.validFill,
|
|
949
|
-
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,
|
|
950
|
-
stroke: "none",
|
|
951
|
-
onPointerDown: (e) => onPiecePointerDown(e, p)
|
|
952
|
-
}
|
|
953
|
-
), showBorders);
|
|
954
|
-
}));
|
|
955
|
-
}),
|
|
1107
|
+
/* @__PURE__ */ React.createElement(React.Fragment, null, renderSilhouettes(), renderPieces()) ,
|
|
956
1108
|
layout.sectors.map((s) => {
|
|
957
1109
|
const sectorCfg = controller.state.cfg.sectors.find((ss) => ss.id === s.id);
|
|
958
1110
|
if (showTangramDecomposition && sectorCfg?.silhouette.primitiveDecomposition) {
|
|
@@ -960,23 +1112,32 @@ function BoardView(props) {
|
|
|
960
1112
|
const rect = rectForBand(layout, s, "silhouette", 1);
|
|
961
1113
|
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
962
1114
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, { cx: rect.cx, cy: rect.cy });
|
|
963
|
-
return /* @__PURE__ */ React.createElement("g", { key: `sil-decomposed-${s.id}`, pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
964
|
-
|
|
965
|
-
{
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
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
|
+
}
|
|
978
1129
|
}
|
|
979
|
-
|
|
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
|
+
}));
|
|
980
1141
|
} else {
|
|
981
1142
|
const placedPolys = placedSilBySector.get(s.id) ?? [];
|
|
982
1143
|
if (!placedPolys.length) return null;
|
|
@@ -1067,7 +1228,25 @@ function BoardView(props) {
|
|
|
1067
1228
|
const by = layout.cy + blueprintRingR * Math.sin(theta);
|
|
1068
1229
|
return renderBlueprintGlyph(bp, bx, by);
|
|
1069
1230
|
}),
|
|
1070
|
-
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
|
+
})
|
|
1071
1250
|
);
|
|
1072
1251
|
}
|
|
1073
1252
|
|
|
@@ -2952,7 +3131,10 @@ function GameBoard(props) {
|
|
|
2952
3131
|
onPieceRemove,
|
|
2953
3132
|
onInteraction,
|
|
2954
3133
|
onTrialEnd,
|
|
2955
|
-
onControllerReady
|
|
3134
|
+
onControllerReady,
|
|
3135
|
+
usePrimitiveColorsBlueprints,
|
|
3136
|
+
usePrimitiveColorsTargets,
|
|
3137
|
+
primitiveColorIndices
|
|
2956
3138
|
} = props;
|
|
2957
3139
|
const [timeRemaining, setTimeRemaining] = React.useState(timeLimitMs > 0 ? Math.floor(timeLimitMs / 1e3) : 0);
|
|
2958
3140
|
const controller = React.useMemo(() => {
|
|
@@ -3273,19 +3455,28 @@ function GameBoard(props) {
|
|
|
3273
3455
|
const headerStyle = {
|
|
3274
3456
|
display: "flex",
|
|
3275
3457
|
flexDirection: "row",
|
|
3276
|
-
justifyContent: "
|
|
3458
|
+
justifyContent: "center",
|
|
3277
3459
|
alignItems: "center",
|
|
3278
3460
|
padding: "20px",
|
|
3279
|
-
background:
|
|
3461
|
+
background: CONFIG.color.background,
|
|
3280
3462
|
flex: "0 0 auto"
|
|
3281
3463
|
};
|
|
3464
|
+
const headerContentStyle = {
|
|
3465
|
+
position: "relative",
|
|
3466
|
+
width: `${svgDimensions.width}px`,
|
|
3467
|
+
maxWidth: "100%",
|
|
3468
|
+
display: "flex",
|
|
3469
|
+
justifyContent: "center",
|
|
3470
|
+
alignItems: "center"
|
|
3471
|
+
};
|
|
3282
3472
|
const instructionsStyle = {
|
|
3283
|
-
flexGrow: 1,
|
|
3284
3473
|
fontSize: "20px",
|
|
3285
3474
|
lineHeight: 1.5,
|
|
3286
|
-
|
|
3475
|
+
textAlign: "center"
|
|
3287
3476
|
};
|
|
3288
3477
|
const timerStyle = {
|
|
3478
|
+
position: "absolute",
|
|
3479
|
+
right: 0,
|
|
3289
3480
|
fontSize: "24px",
|
|
3290
3481
|
fontWeight: "bold",
|
|
3291
3482
|
fontFamily: "monospace",
|
|
@@ -3300,14 +3491,14 @@ function GameBoard(props) {
|
|
|
3300
3491
|
justifyContent: "center",
|
|
3301
3492
|
overflow: "hidden"
|
|
3302
3493
|
};
|
|
3303
|
-
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(
|
|
3494
|
+
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(
|
|
3304
3495
|
"div",
|
|
3305
3496
|
{
|
|
3306
3497
|
className: "tangram-instructions",
|
|
3307
3498
|
style: instructionsStyle,
|
|
3308
3499
|
dangerouslySetInnerHTML: { __html: instructions }
|
|
3309
3500
|
}
|
|
3310
|
-
), 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(
|
|
3501
|
+
), 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(
|
|
3311
3502
|
BoardView,
|
|
3312
3503
|
{
|
|
3313
3504
|
controller,
|
|
@@ -3335,6 +3526,9 @@ function GameBoard(props) {
|
|
|
3335
3526
|
onCenterBadgePointerDown,
|
|
3336
3527
|
showTangramDecomposition: showTangramDecomposition ?? false,
|
|
3337
3528
|
scaleS,
|
|
3529
|
+
usePrimitiveColorsBlueprints: usePrimitiveColorsBlueprints ?? false,
|
|
3530
|
+
usePrimitiveColorsTargets: usePrimitiveColorsTargets ?? false,
|
|
3531
|
+
primitiveColorIndices: primitiveColorIndices ?? [0, 1, 2, 3, 4],
|
|
3338
3532
|
...eventCallbacks
|
|
3339
3533
|
}
|
|
3340
3534
|
))));
|
|
@@ -3554,7 +3748,10 @@ function startConstructionTrial(display_element, params, _jsPsych) {
|
|
|
3554
3748
|
trialParams,
|
|
3555
3749
|
...params.instructions && { instructions: params.instructions },
|
|
3556
3750
|
...params.onInteraction && { onInteraction: params.onInteraction },
|
|
3557
|
-
...params.onTrialEnd && { onTrialEnd: params.onTrialEnd }
|
|
3751
|
+
...params.onTrialEnd && { onTrialEnd: params.onTrialEnd },
|
|
3752
|
+
...params.usePrimitiveColorsBlueprints !== void 0 && { usePrimitiveColorsBlueprints: params.usePrimitiveColorsBlueprints },
|
|
3753
|
+
...params.usePrimitiveColorsTargets !== void 0 && { usePrimitiveColorsTargets: params.usePrimitiveColorsTargets },
|
|
3754
|
+
...params.primitiveColorIndices !== void 0 && { primitiveColorIndices: params.primitiveColorIndices }
|
|
3558
3755
|
};
|
|
3559
3756
|
const root = createRoot(display_element);
|
|
3560
3757
|
root.render(React.createElement(GameBoard, gameBoardProps));
|
|
@@ -3639,6 +3836,24 @@ const info$2 = {
|
|
|
3639
3836
|
type: ParameterType.FUNCTION,
|
|
3640
3837
|
default: void 0,
|
|
3641
3838
|
description: "Callback when trial completes with full data"
|
|
3839
|
+
},
|
|
3840
|
+
/** Whether to use distinct colors for each primitive shape type in blueprints */
|
|
3841
|
+
use_primitive_colors_blueprints: {
|
|
3842
|
+
type: ParameterType.BOOL,
|
|
3843
|
+
default: false,
|
|
3844
|
+
description: "Whether each primitive shape type should have its own distinct color in the blueprint dock area"
|
|
3845
|
+
},
|
|
3846
|
+
/** Whether to use distinct colors for each primitive shape type in target tangrams */
|
|
3847
|
+
use_primitive_colors_targets: {
|
|
3848
|
+
type: ParameterType.BOOL,
|
|
3849
|
+
default: false,
|
|
3850
|
+
description: "Whether each primitive shape type should have its own distinct color in target tangram silhouettes"
|
|
3851
|
+
},
|
|
3852
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
3853
|
+
primitive_color_indices: {
|
|
3854
|
+
type: ParameterType.OBJECT,
|
|
3855
|
+
default: [0, 1, 2, 3, 4],
|
|
3856
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
3642
3857
|
}
|
|
3643
3858
|
},
|
|
3644
3859
|
data: {
|
|
@@ -3704,7 +3919,10 @@ class TangramConstructPlugin {
|
|
|
3704
3919
|
show_tangram_decomposition: trial.show_tangram_decomposition,
|
|
3705
3920
|
instructions: trial.instructions,
|
|
3706
3921
|
onInteraction: trial.onInteraction,
|
|
3707
|
-
onTrialEnd: wrappedOnTrialEnd
|
|
3922
|
+
onTrialEnd: wrappedOnTrialEnd,
|
|
3923
|
+
usePrimitiveColorsBlueprints: trial.use_primitive_colors_blueprints,
|
|
3924
|
+
usePrimitiveColorsTargets: trial.use_primitive_colors_targets,
|
|
3925
|
+
primitiveColorIndices: trial.primitive_color_indices
|
|
3708
3926
|
};
|
|
3709
3927
|
const { root, display_element: element, jsPsych } = startConstructionTrial(display_element, params, this.jsPsych);
|
|
3710
3928
|
element.__reactContext = { root, jsPsych };
|
|
@@ -3722,7 +3940,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3722
3940
|
quickstashMacros,
|
|
3723
3941
|
primitiveOrder,
|
|
3724
3942
|
onInteraction,
|
|
3725
|
-
onTrialEnd
|
|
3943
|
+
onTrialEnd,
|
|
3944
|
+
usePrimitiveColorsBlueprints,
|
|
3945
|
+
primitiveColorIndices
|
|
3726
3946
|
} = params;
|
|
3727
3947
|
const { onInteraction: _, onTrialEnd: __, ...trialParams } = params;
|
|
3728
3948
|
const PRIMITIVE_BLUEPRINTS_ORDERED = [...PRIMITIVE_BLUEPRINTS].sort((a, b) => primitiveOrder.indexOf(a.kind) - primitiveOrder.indexOf(b.kind));
|
|
@@ -3814,7 +4034,9 @@ function startPrepTrial(display_element, params, jsPsych) {
|
|
|
3814
4034
|
...params.instructions && { instructions: params.instructions },
|
|
3815
4035
|
onControllerReady: handleControllerReady,
|
|
3816
4036
|
...onInteraction && { onInteraction },
|
|
3817
|
-
...onTrialEnd && { onTrialEnd }
|
|
4037
|
+
...onTrialEnd && { onTrialEnd },
|
|
4038
|
+
...usePrimitiveColorsBlueprints !== void 0 && { usePrimitiveColorsBlueprints },
|
|
4039
|
+
...primitiveColorIndices !== void 0 && { primitiveColorIndices }
|
|
3818
4040
|
}));
|
|
3819
4041
|
return { root, display_element, jsPsych };
|
|
3820
4042
|
}
|
|
@@ -3880,6 +4102,18 @@ const info$1 = {
|
|
|
3880
4102
|
onTrialEnd: {
|
|
3881
4103
|
type: ParameterType.FUNCTION,
|
|
3882
4104
|
default: void 0
|
|
4105
|
+
},
|
|
4106
|
+
/** Whether to use distinct colors for each primitive shape type in blueprints */
|
|
4107
|
+
use_primitive_colors_blueprints: {
|
|
4108
|
+
type: ParameterType.BOOL,
|
|
4109
|
+
default: false,
|
|
4110
|
+
description: "Whether each primitive shape type should have its own distinct color in the blueprint dock area"
|
|
4111
|
+
},
|
|
4112
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
4113
|
+
primitive_color_indices: {
|
|
4114
|
+
type: ParameterType.OBJECT,
|
|
4115
|
+
default: [0, 1, 2, 3, 4],
|
|
4116
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
3883
4117
|
}
|
|
3884
4118
|
},
|
|
3885
4119
|
data: {
|
|
@@ -3924,7 +4158,9 @@ class TangramPrepPlugin {
|
|
|
3924
4158
|
primitiveOrder: trial.primitive_order,
|
|
3925
4159
|
instructions: trial.instructions,
|
|
3926
4160
|
onInteraction: trial.onInteraction,
|
|
3927
|
-
onTrialEnd: wrappedOnTrialEnd
|
|
4161
|
+
onTrialEnd: wrappedOnTrialEnd,
|
|
4162
|
+
usePrimitiveColorsBlueprints: trial.use_primitive_colors_blueprints,
|
|
4163
|
+
primitiveColorIndices: trial.primitive_color_indices
|
|
3928
4164
|
};
|
|
3929
4165
|
const { root, display_element: element, jsPsych } = startPrepTrial(display_element, params, this.jsPsych);
|
|
3930
4166
|
element.__reactContext = { root, jsPsych };
|
|
@@ -3944,6 +4180,8 @@ function NBackView({ params }) {
|
|
|
3944
4180
|
instructions,
|
|
3945
4181
|
button_text,
|
|
3946
4182
|
duration,
|
|
4183
|
+
usePrimitiveColors,
|
|
4184
|
+
primitiveColorIndices,
|
|
3947
4185
|
onTrialEnd
|
|
3948
4186
|
} = params;
|
|
3949
4187
|
const trialStartTime = useRef(Date.now());
|
|
@@ -3999,7 +4237,12 @@ function NBackView({ params }) {
|
|
|
3999
4237
|
...data,
|
|
4000
4238
|
accuracy,
|
|
4001
4239
|
tangram_id: tangram.tangramID,
|
|
4002
|
-
is_match: isMatch
|
|
4240
|
+
is_match: isMatch,
|
|
4241
|
+
show_tangram_decomposition,
|
|
4242
|
+
use_primitive_colors: usePrimitiveColors,
|
|
4243
|
+
primitive_color_indices: primitiveColorIndices,
|
|
4244
|
+
duration,
|
|
4245
|
+
button_text
|
|
4003
4246
|
};
|
|
4004
4247
|
if (onTrialEnd) {
|
|
4005
4248
|
onTrialEnd(trialData);
|
|
@@ -4053,35 +4296,77 @@ function NBackView({ params }) {
|
|
|
4053
4296
|
if (show_tangram_decomposition) {
|
|
4054
4297
|
const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
|
|
4055
4298
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, centerPos);
|
|
4056
|
-
return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4299
|
+
return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
4300
|
+
const primInfo = primitiveDecomposition[i];
|
|
4301
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
4302
|
+
if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
|
|
4303
|
+
const kindToIndex = {
|
|
4304
|
+
"square": 0,
|
|
4305
|
+
"smalltriangle": 1,
|
|
4306
|
+
"parallelogram": 2,
|
|
4307
|
+
"medtriangle": 3,
|
|
4308
|
+
"largetriangle": 4
|
|
4309
|
+
};
|
|
4310
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
4311
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
4312
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
4313
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
4314
|
+
if (color) {
|
|
4315
|
+
fillColor = color;
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4071
4318
|
}
|
|
4072
|
-
|
|
4319
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, { key: `prim-${i}` }, /* @__PURE__ */ React.createElement(
|
|
4320
|
+
"path",
|
|
4321
|
+
{
|
|
4322
|
+
d: pathD(scaledPoly),
|
|
4323
|
+
fill: fillColor,
|
|
4324
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
4325
|
+
stroke: "none"
|
|
4326
|
+
}
|
|
4327
|
+
), /* @__PURE__ */ React.createElement(
|
|
4328
|
+
"path",
|
|
4329
|
+
{
|
|
4330
|
+
d: pathD(scaledPoly),
|
|
4331
|
+
fill: "none",
|
|
4332
|
+
stroke: CONFIG.color.tangramDecomposition.stroke,
|
|
4333
|
+
strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
|
|
4334
|
+
}
|
|
4335
|
+
));
|
|
4336
|
+
}));
|
|
4073
4337
|
} else {
|
|
4074
4338
|
const placedPolys = placeSilhouetteGridAlignedAsPolys(mask, scaleS, centerPos);
|
|
4075
|
-
return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) =>
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4339
|
+
return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
|
|
4340
|
+
const primInfo = primitiveDecomposition[i];
|
|
4341
|
+
let fillColor = CONFIG.color.silhouetteMask;
|
|
4342
|
+
if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
|
|
4343
|
+
const kindToIndex = {
|
|
4344
|
+
"square": 0,
|
|
4345
|
+
"smalltriangle": 1,
|
|
4346
|
+
"parallelogram": 2,
|
|
4347
|
+
"medtriangle": 3,
|
|
4348
|
+
"largetriangle": 4
|
|
4349
|
+
};
|
|
4350
|
+
const primitiveIndex = kindToIndex[primInfo.kind];
|
|
4351
|
+
if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
|
|
4352
|
+
const colorIndex = primitiveColorIndices[primitiveIndex];
|
|
4353
|
+
const color = CONFIG.color.primitiveColors[colorIndex];
|
|
4354
|
+
if (color) {
|
|
4355
|
+
fillColor = color;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4083
4358
|
}
|
|
4084
|
-
|
|
4359
|
+
return /* @__PURE__ */ React.createElement(
|
|
4360
|
+
"path",
|
|
4361
|
+
{
|
|
4362
|
+
key: `sil-${i}`,
|
|
4363
|
+
d: pathD(scaledPoly),
|
|
4364
|
+
fill: fillColor,
|
|
4365
|
+
opacity: CONFIG.opacity.silhouetteMask,
|
|
4366
|
+
stroke: "none"
|
|
4367
|
+
}
|
|
4368
|
+
);
|
|
4369
|
+
}));
|
|
4085
4370
|
}
|
|
4086
4371
|
};
|
|
4087
4372
|
return /* @__PURE__ */ React.createElement("div", { style: {
|
|
@@ -4180,6 +4465,18 @@ const info = {
|
|
|
4180
4465
|
default: 3e3,
|
|
4181
4466
|
description: "Duration in milliseconds to display tangram and accept responses"
|
|
4182
4467
|
},
|
|
4468
|
+
/** Whether to use distinct colors for each primitive shape type */
|
|
4469
|
+
use_primitive_colors: {
|
|
4470
|
+
type: ParameterType.BOOL,
|
|
4471
|
+
default: false,
|
|
4472
|
+
description: "Whether each primitive shape type should have its own distinct color in the displayed tangram"
|
|
4473
|
+
},
|
|
4474
|
+
/** Indices mapping primitives to colors from the color palette */
|
|
4475
|
+
primitive_color_indices: {
|
|
4476
|
+
type: ParameterType.OBJECT,
|
|
4477
|
+
default: [0, 1, 2, 3, 4],
|
|
4478
|
+
description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
|
|
4479
|
+
},
|
|
4183
4480
|
/** Callback fired when trial ends */
|
|
4184
4481
|
onTrialEnd: {
|
|
4185
4482
|
type: ParameterType.FUNCTION,
|
|
@@ -4246,6 +4543,8 @@ class TangramNBackPlugin {
|
|
|
4246
4543
|
instructions: trial.instructions,
|
|
4247
4544
|
button_text: trial.button_text,
|
|
4248
4545
|
duration: trial.duration,
|
|
4546
|
+
usePrimitiveColors: trial.use_primitive_colors,
|
|
4547
|
+
primitiveColorIndices: trial.primitive_color_indices,
|
|
4249
4548
|
onTrialEnd: wrappedOnTrialEnd
|
|
4250
4549
|
};
|
|
4251
4550
|
const { root, display_element: element, jsPsych } = startNBackTrial(display_element, params, this.jsPsych);
|