Skip to content

Commit b614f4f

Browse files
authored
Merge pull request #113 from immersivecognition/personal/jack/2022-improvements
feb 2022 improvements
2 parents 2a2f8ad + fd965cb commit b614f4f

19 files changed

+307
-139
lines changed

Assets/UXF/Scripts/Etc/Block.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace UXF
88
/// <summary>
99
/// A set of trials, often used to group a number of consecutive Trial objects that share something in common.
1010
/// </summary>
11-
public class Block : ISettingsContainer
11+
public class Block : IExperimentUnit
1212
{
1313
/// <summary>
1414
/// List of trials associated with this block
@@ -33,13 +33,21 @@ public class Block : ISettingsContainer
3333
/// <summary>
3434
/// Block settings. These will be overridden by trial settings if set.
3535
/// </summary>
36-
public Settings settings { get; private set; }
36+
public Settings settings { get; protected set; }
3737

3838
/// <summary>
3939
/// The session associated with this block
4040
/// </summary>
4141
public Session session { get; private set; }
4242

43+
/// <summary>
44+
/// Should data be saved for this session?
45+
/// </summary>
46+
public bool saveData
47+
{
48+
get => settings.GetBool(Constants.SAVE_DATA_SETTING_NAME, true);
49+
set => settings.SetValue(Constants.SAVE_DATA_SETTING_NAME, value);
50+
}
4351

4452
/// <summary>
4553
/// Create a block with a given number of trials under a given session

Assets/UXF/Scripts/Etc/Constants.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using UnityEngine;
2+
3+
namespace UXF
4+
{
5+
public static class Constants
6+
{
7+
public const string SAVE_DATA_SETTING_NAME = "_UXFInternal_SaveData";
8+
}
9+
}

Assets/UXF/Scripts/Etc/Constants.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/UXF/Scripts/Etc/Editor/TrackerEditor.cs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44

55
namespace UXF.EditorUtils
66
{
7-
[CustomEditor(typeof(UXF.Tracker), true)]
7+
[CustomEditor(typeof(Tracker), true)]
88
[CanEditMultipleObjects]
99
public class TrackerEditor : Editor
1010
{
11-
SerializedProperty customHeader, measurementDescriptor;
11+
GUIStyle smallText = new GUIStyle();
12+
Tracker thisTracker;
1213

1314
void OnEnable()
1415
{
15-
customHeader = serializedObject.FindProperty("customHeader");
16-
measurementDescriptor = serializedObject.FindProperty("measurementDescriptor");
16+
smallText.font = EditorStyles.miniFont;
17+
smallText.fontSize = 9;
18+
thisTracker = (Tracker)serializedObject.targetObject;
1719
}
1820

1921
public override void OnInspectorGUI()
@@ -31,34 +33,38 @@ public override void OnInspectorGUI()
3133
{
3234
if (field.IsPublic || field.GetCustomAttribute(typeof(SerializeField)) != null)
3335
{
34-
if (field.Name != measurementDescriptor.name && field.Name != customHeader.name)
35-
{
36-
var prop = serializedObject.FindProperty(field.Name);
37-
EditorGUILayout.PropertyField(prop);
38-
}
36+
var prop = serializedObject.FindProperty(field.Name);
37+
EditorGUILayout.PropertyField(prop);
3938
}
4039
}
4140

4241
EditorGUILayout.Space();
43-
GUI.enabled = false;
4442

45-
EditorGUILayout.LabelField(customHeader.displayName);
43+
EditorGUILayout.LabelField("Custom Header");
44+
EditorGUILayout.TextField("(\"time\" is added automatically)", smallText);
4645
EditorGUI.indentLevel += 1;
47-
48-
foreach (SerializedProperty element in customHeader)
49-
{
50-
EditorGUILayout.TextField(element.stringValue);
51-
}
46+
GUI.enabled = false;
47+
EditorGUILayout.TextField(string.Join(", ", thisTracker.CustomHeader));
48+
GUI.enabled = true;
5249
EditorGUI.indentLevel -= 1;
5350

5451
EditorGUILayout.Space();
5552

56-
EditorGUILayout.LabelField(measurementDescriptor.displayName);
53+
EditorGUILayout.LabelField("Measurement Descriptor");
5754
EditorGUI.indentLevel += 1;
58-
EditorGUILayout.TextField(measurementDescriptor.stringValue);
55+
GUI.enabled = false;
56+
EditorGUILayout.TextField(thisTracker.MeasurementDescriptor);
57+
GUI.enabled = true;
5958
EditorGUI.indentLevel -= 1;
6059

60+
EditorGUILayout.Space();
61+
EditorGUILayout.LabelField("Example Filename");
62+
EditorGUI.indentLevel += 1;
63+
GUI.enabled = false;
64+
EditorGUILayout.TextField(string.Format("{0}_T001.csv", thisTracker.DataName));
6165
GUI.enabled = true;
66+
EditorGUI.indentLevel -= 1;
67+
6268
serializedObject.ApplyModifiedProperties();
6369
}
6470
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Linq;
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
using UnityEngine;
6+
7+
namespace UXF
8+
{
9+
/// <summary>
10+
/// Represents a unit of an experiment.
11+
/// </summary>
12+
public interface IExperimentUnit : ISettingsContainer
13+
{
14+
/// <summary>
15+
/// Sets wether data should be saved for this experiment unit.
16+
/// </summary>
17+
bool saveData { get; }
18+
}
19+
}

Assets/UXF/Scripts/Etc/IExperimentUnit.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/UXF/Scripts/Etc/Tracker.cs

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.IO;
44
using System.Collections.Generic;
5+
using System.Linq;
56

67
namespace UXF
78
{
@@ -10,6 +11,9 @@ namespace UXF
1011
/// </summary>
1112
public abstract class Tracker : MonoBehaviour
1213
{
14+
private bool recording = false;
15+
private static string[] baseHeaders = new string[] { "time" };
16+
1317
/// <summary>
1418
/// Name of the object used in saving
1519
/// </summary>
@@ -18,47 +22,29 @@ public abstract class Tracker : MonoBehaviour
1822
/// <summary>
1923
/// Description of the type of measurement this tracker will perform.
2024
/// </summary>
21-
[Tooltip("Description of the type of measurement this tracker will perform.")]
22-
public string measurementDescriptor;
25+
public abstract string MeasurementDescriptor { get; }
2326

2427
/// <summary>
25-
/// Custom column headers for tracked objects. Time is added automatically
28+
/// Custom column headers for tracked objects.
2629
/// </summary>
27-
[Tooltip("Custom column headers for each measurement.")]
28-
public string[] customHeader = new string[] { };
30+
public abstract IEnumerable<string> CustomHeader { get; }
2931

3032
/// <summary>
3133
/// A name used when saving the data from this tracker.
3234
/// </summary>
33-
public string dataName
35+
public string DataName
3436
{
3537
get
3638
{
37-
Debug.AssertFormat(measurementDescriptor.Length > 0, "No measurement descriptor has been specified for this Tracker!");
38-
return string.Join("_", new string[]{ objectName, measurementDescriptor });
39+
Debug.AssertFormat(MeasurementDescriptor.Length > 0, "No measurement descriptor has been specified for this Tracker!");
40+
return string.Join("_", new string[]{ objectName, MeasurementDescriptor });
3941
}
4042
}
4143

42-
private bool recording;
43-
4444
public bool Recording { get { return recording; } }
4545

46-
public UXFDataTable data { get; private set; } = new UXFDataTable();
46+
public UXFDataTable Data { get; private set; } = new UXFDataTable();
4747

48-
/// <summary>
49-
/// The header that will go at the top of the output file associated with this tracker
50-
/// </summary>
51-
/// <returns></returns>
52-
public string[] header
53-
{
54-
get
55-
{
56-
var newHeader = new string[customHeader.Length + 1];
57-
newHeader[0] = "time";
58-
customHeader.CopyTo(newHeader, 1);
59-
return newHeader;
60-
}
61-
}
6248

6349
/// <summary>
6450
/// When the tracker should take measurements.
@@ -70,7 +56,6 @@ public string[] header
7056
void Reset()
7157
{
7258
objectName = gameObject.name.Replace(" ", "_").ToLower();
73-
SetupDescriptorAndHeader();
7459
}
7560

7661
// called by unity just before rendering the frame
@@ -90,19 +75,20 @@ void FixedUpdate()
9075
/// </summary>
9176
public void RecordRow()
9277
{
93-
if (!recording) throw new System.InvalidOperationException("Tracker measurements cannot be taken when not in a trial!");
78+
if (!recording) throw new System.InvalidOperationException("Tracker measurements cannot be taken when not recording!");
9479

9580
UXFDataRow newRow = GetCurrentValues();
9681
newRow.Add(("time", Time.time));
97-
data.AddCompleteRow(newRow);
82+
Data.AddCompleteRow(newRow);
9883
}
9984

10085
/// <summary>
10186
/// Begins recording.
10287
/// </summary>
10388
public void StartRecording()
10489
{
105-
data = new UXFDataTable(header);
90+
var header = baseHeaders.Concat(CustomHeader);
91+
Data = new UXFDataTable(header.ToArray());
10692
recording = true;
10793
}
10894

@@ -120,11 +106,6 @@ public void StopRecording()
120106
/// <returns></returns>
121107
protected abstract UXFDataRow GetCurrentValues();
122108

123-
/// <summary>
124-
/// Override this method and define your own descriptor and header.
125-
/// </summary>
126-
protected abstract void SetupDescriptorAndHeader();
127-
128109
}
129110

130111
/// <summary>

Assets/UXF/Scripts/Etc/Trial.cs

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace UXF
1313
/// The base unit of experiments. A Trial is usually a singular attempt at a task by a participant after/during the presentation of a stimulus.
1414
/// </summary>
1515
[Serializable]
16-
public class Trial : ISettingsContainer, IDataAssociatable
16+
public class Trial : IExperimentUnit, IDataAssociatable
1717
{
1818

1919
/// <summary>
@@ -46,7 +46,16 @@ public class Trial : ISettingsContainer, IDataAssociatable
4646
/// <summary>
4747
/// Trial settings. These will override block settings if set.
4848
/// </summary>
49-
public Settings settings { get; private set; }
49+
public Settings settings { get; protected set; }
50+
51+
/// <summary>
52+
/// Should data be saved for this session?
53+
/// </summary>
54+
public bool saveData
55+
{
56+
get => settings.GetBool(Constants.SAVE_DATA_SETTING_NAME, true);
57+
set => settings.SetValue(Constants.SAVE_DATA_SETTING_NAME, value);
58+
}
5059

5160
/// <summary>
5261
/// Dictionary of results in a order.
@@ -121,34 +130,10 @@ public void End()
121130
status = TrialStatus.Done;
122131
endTime = Time.time;
123132
result["end_time"] = endTime;
124-
125-
// check no duplicate trackers
126-
List<string> duplicateTrackers = session.trackedObjects.Where(tracker => tracker != null)
127-
.GroupBy(tracker => tracker.dataName)
128-
.Where(g => g.Count() > 1)
129-
.Select(y => y.Key)
130-
.ToList();
131-
132-
if (duplicateTrackers.Any()) throw new InvalidOperationException(string.Format("Two or more trackers in the Tracked Objects field in the Session Inspector have the following object name and descriptor pair, please change the object name fields on the trackers to make them unique: {0}", string.Join(",", duplicateTrackers)));
133-
134-
// log tracked objects
135-
foreach (Tracker tracker in session.trackedObjects)
136-
{
137-
try
138-
{
139-
tracker.StopRecording();
140-
SaveDataTable(tracker.data, tracker.dataName, dataType: UXFDataType.Trackers);
141-
}
142-
catch (NullReferenceException)
143-
{
144-
Utilities.UXFDebugLogWarning("An item in the Tracked Objects field of the UXF session if empty (null)!");
145-
}
146-
}
147-
148-
// log any settings we need to for this trial
149-
foreach (string s in session.settingsToLog)
133+
134+
if (saveData)
150135
{
151-
result[s] = settings.GetObject(s, string.Empty);
136+
SaveData();
152137
}
153138

154139
session.onTrialEnd.Invoke(this);
@@ -262,7 +247,37 @@ public void SaveBytes(byte[] bytes, string dataName, UXFDataType dataType = UXFD
262247
}
263248
}
264249

250+
private void SaveData()
251+
{
252+
// check no duplicate trackers
253+
List<string> duplicateTrackers = session.trackedObjects.Where(tracker => tracker != null)
254+
.GroupBy(tracker => tracker.DataName)
255+
.Where(g => g.Count() > 1)
256+
.Select(y => y.Key)
257+
.ToList();
258+
259+
if (duplicateTrackers.Any()) throw new InvalidOperationException(string.Format("Two or more trackers in the Tracked Objects field in the Session Inspector have the following object name and descriptor pair, please change the object name fields on the trackers to make them unique: {0}", string.Join(",", duplicateTrackers)));
260+
261+
// log tracked objects
262+
foreach (Tracker tracker in session.trackedObjects)
263+
{
264+
try
265+
{
266+
tracker.StopRecording();
267+
SaveDataTable(tracker.Data, tracker.DataName, dataType: UXFDataType.Trackers);
268+
}
269+
catch (NullReferenceException)
270+
{
271+
Utilities.UXFDebugLogWarning("An item in the Tracked Objects field of the UXF session if empty (null)!");
272+
}
273+
}
265274

275+
// log any settings we need to for this trial
276+
foreach (string s in session.settingsToLog)
277+
{
278+
result[s] = settings.GetObject(s, string.Empty);
279+
}
280+
}
266281
}
267282

268283

0 commit comments

Comments
 (0)