Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data goes out of range in the SampleSizeThumbnailNode when changing the global launcher characteristics #301

Closed
Tracked by #1079
samreid opened this issue Apr 19, 2024 · 5 comments

Comments

@samreid
Copy link
Member

samreid commented Apr 19, 2024

Today @matthew-blackman and @catherinecarter and I observed that changing globals like projectileDataLab.global.model.launcherMechanisms.explosion.speedMeanProperty could easily make data appear out of bounds in the SampleSizeThumbnailNode. We discussed that we could compute the range analytically. @matthew-blackman and I implemented this patch and it is working well. There are some TODOs and we may not want to pass the entire model.

We can continue from this patch:

Subject: [PATCH] Vertically flip boat and bottle icon images, see https://github.com/phetsims/buoyancy/issues/141
---
Index: js/sampling/view/SamplingScreenView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingScreenView.ts b/js/sampling/view/SamplingScreenView.ts
--- a/js/sampling/view/SamplingScreenView.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingScreenView.ts	(date 1713548476145)
@@ -155,6 +155,7 @@
       } );
 
     this.accordionBox = new SamplingAccordionBox(
+      model,
       model.histogram,
       model.launcherProperty,
       model.sampleSizeProperty,
Index: js/sampling/view/SampleSizeThumbnailNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SampleSizeThumbnailNode.ts b/js/sampling/view/SampleSizeThumbnailNode.ts
--- a/js/sampling/view/SampleSizeThumbnailNode.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SampleSizeThumbnailNode.ts	(date 1713548590673)
@@ -28,12 +28,15 @@
 import { ZOOM_LEVELS } from '../../common/model/Histogram.js';
 import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
 import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
+import SamplingModel from '../model/SamplingModel.js';
+import Multilink from '../../../../axon/js/Multilink.js';
 
 type SelfOptions = EmptySelfOptions;
 type SampleSizeThumbnailNodeOptions = SelfOptions & WithRequired<NodeOptions, 'tandem'>;
 
 export default class SampleSizeThumbnailNode extends Node {
-  public constructor( thumbnailSampleSize: number,
+  public constructor( model: SamplingModel,
+                      thumbnailSampleSize: number,
                       fieldProperty: TReadOnlyProperty<SamplingField>,
                       fields: SamplingField[],
                       binWidthProperty: TReadOnlyProperty<number>,
@@ -51,32 +54,47 @@
       modelYRange: new Range( 0, 10 )
     } );
 
+    function calculateRangeStdDev( meanSpeed: number, stdDevSpeed: number, meanAngle: number, stdDevAngle: number ): number {
+      const g = 9.81; // acceleration due to gravity in m/s^2
+      const radiansMeanAngle = meanAngle * Math.PI / 180; // convert mean angle from degrees to radians
+      const radiansStdDevAngle = stdDevAngle * Math.PI / 180; // convert standard deviation angle from degrees to radians
+
+      // Partial derivatives evaluated at mean values
+      const partialV = ( 2 * meanSpeed / g ) * Math.sin( 2 * radiansMeanAngle );
+      const partialTheta = ( 2 * meanSpeed ** 2 / g ) * Math.cos( 2 * radiansMeanAngle );
+
+      // Variance components
+      const varV = partialV ** 2 * stdDevSpeed ** 2;
+      const varTheta = partialTheta ** 2 * radiansStdDevAngle ** 2;
+
+      // Total variance of the range
+      const totalVariance = varV + varTheta;
+
+      // Standard deviation of the range
+      const rangeStdDev = Math.sqrt( totalVariance );
+
+      return rangeStdDev;
+    }
+
     // Horizontally zoom in on the thumbnails, centering on the average output for the mystery launcher
     // Each launcher has a different average output, so we need to adjust the range for the thumbnail histogram based on the mystery launcher
-    fieldProperty.link( field => {
 
-      const index = field.launcherProperty.value.launcherNumber;
+    Multilink.multilink( [ model.meanLaunchSpeedProperty, model.standardDeviationSpeedProperty, model.standardDeviationAngleProperty ], ( meanLaunchSpeed, standardDeviationSpeed,
+                                                                                                                                          standardDeviationAngle ) => {
 
-      // The width of the range of the thumbnail histogram
-      const thumbnailDomain =
-        index === 1 ? 20 :
-        index === 2 ? 40 :
-        index === 3 ? 30 :
-        index === 4 ? 19 :
-        index === 5 ? 20 :
-        index === 6 ? 50 :
-        40;
+      const g = 9.8; // TODO: Use same constant
+      const meanRange = Math.pow( meanLaunchSpeed, 2 ) / g * Math.sin( 2 * 30 * Math.PI / 180 );
 
-      const thumbnailMean =
-        index === 1 ? 46 :
-        index === 2 ? 50 :
-        index === 3 ? 55 :
-        index === 4 ? 46 :
-        index === 5 ? 51 :
-        index === 6 ? 55 :
-        50;
+      const standardDeviationRange = calculateRangeStdDev(
+        meanLaunchSpeed,
+        standardDeviationSpeed,
+        30,
+        standardDeviationAngle
+      );
+      console.log( standardDeviationRange );
 
-      const range = new Range( thumbnailMean - thumbnailDomain / 2, thumbnailMean + thumbnailDomain / 2 );
+      const range = new Range( meanRange - 3 * standardDeviationRange, meanRange + 3 * standardDeviationRange );
+      console.log( range );
 
       chartTransform.setModelXRange( range );
     } );
Index: js/common/view/HistogramNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/HistogramNode.ts b/js/common/view/HistogramNode.ts
--- a/js/common/view/HistogramNode.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/view/HistogramNode.ts	(date 1713543649179)
@@ -86,7 +86,7 @@
       // Tuned to make the spacing between the right panel and the histogram match that of the left
       viewWidth: 561,
       viewHeight: 165,
-      modelXRange: new Range( 0, PDLConstants.MAX_FIELD_DISTANCE ),
+      modelXRange: new Range( 20, 80 ),
       modelYRange: new Range( 0, 25 )
     } );
 
Index: js/common/model/PDLModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/PDLModel.ts b/js/common/model/PDLModel.ts
--- a/js/common/model/PDLModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/PDLModel.ts	(date 1713548358669)
@@ -28,6 +28,7 @@
 import StringUnionProperty from '../../../../axon/js/StringUnionProperty.js';
 import TProperty from '../../../../axon/js/TProperty.js';
 import PhetioProperty from '../../../../axon/js/PhetioProperty.js';
+import NumberIO from '../../../../tandem/js/types/NumberIO.js';
 
 // See the documentation at the PDLModel class attributes for more information on these options.
 type SelfOptions<T extends Field> = {
@@ -78,6 +79,9 @@
   public readonly projectileTypeProperty: PhetioProperty<ProjectileType>;
 
   public readonly meanLaunchAngleProperty: TReadOnlyProperty<number>;
+  public readonly meanLaunchSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly launcherHeightProperty: TReadOnlyProperty<number>;
 
@@ -166,6 +170,24 @@
       derive: field => field.meanAngleProperty
     } );
 
+    this.meanLaunchSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.meanSpeedProperty
+    } );
+
+    this.standardDeviationSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationSpeedProperty
+    } );
+
+    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationAngleProperty,
+      tandem: Tandem.OPT_OUT, // TODO: providedOptions.isStandardDeviationAnglePropertyPhetioInstrumented ? providedOptions.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
+      phetioFeatured: true,
+      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
+      phetioValueType: NumberIO,
+      phetioReadOnly: true,
+      phetioState: false
+    } );
+
     this.launcherHeightProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,
       derive: field => field.launchHeightProperty
Index: js/sampling/view/SamplingAccordionBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingAccordionBox.ts b/js/sampling/view/SamplingAccordionBox.ts
--- a/js/sampling/view/SamplingAccordionBox.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingAccordionBox.ts	(date 1713548476151)
@@ -24,6 +24,7 @@
 import Launcher from '../../common/model/Launcher.js';
 import NumberProperty from '../../../../axon/js/NumberProperty.js';
 import Histogram from '../../common/model/Histogram.js';
+import SamplingModel from '../model/SamplingModel.js';
 
 type SelfOptions = EmptySelfOptions;
 
@@ -33,6 +34,7 @@
 export default class SamplingAccordionBox extends HistogramAccordionBox {
 
   public constructor(
+    model: SamplingModel,
     histogram: Histogram,
     launcherProperty: TReadOnlyProperty<Launcher>,
     sampleSizeProperty: TReadOnlyProperty<number>,
@@ -71,22 +73,22 @@
       tandem: thumbnailContainerTandem,
       visiblePropertyOptions: { phetioFeatured: true },
       children: [
-        new SampleSizeThumbnailNode( 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize2ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize5ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize15ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sample40ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
Index: js/common/model/Field.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/Field.ts b/js/common/model/Field.ts
--- a/js/common/model/Field.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/Field.ts	(date 1713548590680)
@@ -67,10 +67,10 @@
   // These values are DynamicProperties that are determined by the Launcher, see Launcher.ts and implementation-notes.md
 
   // Specifies the average speed of launched projectiles
-  private readonly meanSpeedProperty: TReadOnlyProperty<number>;
+  public readonly meanSpeedProperty: TReadOnlyProperty<number>;
 
   // Specifies the speed standard deviation for launched projectiles.
-  private readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
 
   // Indicates the current value for the standard deviation of the angle of launch.
   public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
Index: js/common-vsm/model/VSMModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common-vsm/model/VSMModel.ts b/js/common-vsm/model/VSMModel.ts
--- a/js/common-vsm/model/VSMModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common-vsm/model/VSMModel.ts	(date 1713548358677)
@@ -64,7 +64,6 @@
 
   // The angleStabilityProperty represents the amount of angle stabilization applied to the launcher. The value is between 0 and 1, where 0 means minimum stabilization and 1 means maximum stabilization.
   public readonly angleStabilityProperty: PhetioProperty<number>;
-  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly selectedProjectileNumberProperty: PhetioProperty<number>;
   public readonly selectedProjectileProperty: TReadOnlyProperty<Projectile | null>;
@@ -131,16 +130,6 @@
       phetioReadOnly: true,
       phetioState: false
     } );
-
-    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
-      derive: field => field.standardDeviationAngleProperty,
-      tandem: options.isStandardDeviationAnglePropertyPhetioInstrumented ? options.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
-      phetioFeatured: true,
-      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
-      phetioValueType: NumberIO,
-      phetioReadOnly: true,
-      phetioState: false
-    } );
 
     this.selectedProjectileNumberProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,
@samreid
Copy link
Member Author

samreid commented Apr 22, 2024

New patch that incorporates the bin width size to make sure there is enough padding on either side:

Subject: [PATCH] Vertically flip boat and bottle icon images, see https://github.com/phetsims/buoyancy/issues/141
---
Index: js/sampling/view/SamplingScreenView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingScreenView.ts b/js/sampling/view/SamplingScreenView.ts
--- a/js/sampling/view/SamplingScreenView.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingScreenView.ts	(date 1713801041259)
@@ -155,6 +155,7 @@
       } );
 
     this.accordionBox = new SamplingAccordionBox(
+      model,
       model.histogram,
       model.launcherProperty,
       model.sampleSizeProperty,
Index: js/sampling/view/SampleSizeThumbnailNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SampleSizeThumbnailNode.ts b/js/sampling/view/SampleSizeThumbnailNode.ts
--- a/js/sampling/view/SampleSizeThumbnailNode.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SampleSizeThumbnailNode.ts	(date 1713801857732)
@@ -28,12 +28,15 @@
 import { ZOOM_LEVELS } from '../../common/model/Histogram.js';
 import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
 import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
+import SamplingModel from '../model/SamplingModel.js';
+import Multilink from '../../../../axon/js/Multilink.js';
 
 type SelfOptions = EmptySelfOptions;
 type SampleSizeThumbnailNodeOptions = SelfOptions & WithRequired<NodeOptions, 'tandem'>;
 
 export default class SampleSizeThumbnailNode extends Node {
-  public constructor( thumbnailSampleSize: number,
+  public constructor( model: SamplingModel,
+                      thumbnailSampleSize: number,
                       fieldProperty: TReadOnlyProperty<SamplingField>,
                       fields: SamplingField[],
                       binWidthProperty: TReadOnlyProperty<number>,
@@ -51,32 +54,49 @@
       modelYRange: new Range( 0, 10 )
     } );
 
+    function calculateRangeStdDev( meanSpeed: number, stdDevSpeed: number, meanAngle: number, stdDevAngle: number ): number {
+      const g = 9.81; // acceleration due to gravity in m/s^2
+      const radiansMeanAngle = meanAngle * Math.PI / 180; // convert mean angle from degrees to radians
+      const radiansStdDevAngle = stdDevAngle * Math.PI / 180; // convert standard deviation angle from degrees to radians
+
+      // Partial derivatives evaluated at mean values
+      const partialV = ( 2 * meanSpeed / g ) * Math.sin( 2 * radiansMeanAngle );
+      const partialTheta = ( 2 * meanSpeed ** 2 / g ) * Math.cos( 2 * radiansMeanAngle );
+
+      // Variance components
+      const varV = partialV ** 2 * stdDevSpeed ** 2;
+      const varTheta = partialTheta ** 2 * radiansStdDevAngle ** 2;
+
+      // Total variance of the range
+      const totalVariance = varV + varTheta;
+
+      // Standard deviation of the range
+      const rangeStdDev = Math.sqrt( totalVariance );
+
+      return rangeStdDev;
+    }
+
     // Horizontally zoom in on the thumbnails, centering on the average output for the mystery launcher
     // Each launcher has a different average output, so we need to adjust the range for the thumbnail histogram based on the mystery launcher
-    fieldProperty.link( field => {
 
-      const index = field.launcherProperty.value.launcherNumber;
+    Multilink.multilink( [ model.histogram.binWidthProperty, model.meanLaunchSpeedProperty, model.standardDeviationSpeedProperty, model.standardDeviationAngleProperty ], ( binWidth, meanLaunchSpeed, standardDeviationSpeed,
+                                                                                                                                                                            standardDeviationAngle ) => {
 
-      // The width of the range of the thumbnail histogram
-      const thumbnailDomain =
-        index === 1 ? 20 :
-        index === 2 ? 40 :
-        index === 3 ? 30 :
-        index === 4 ? 19 :
-        index === 5 ? 20 :
-        index === 6 ? 50 :
-        40;
+      const g = 9.8; // TODO: Use same constant
+      const meanRange = Math.pow( meanLaunchSpeed, 2 ) / g * Math.sin( 2 * 30 * Math.PI / 180 );
 
-      const thumbnailMean =
-        index === 1 ? 46 :
-        index === 2 ? 50 :
-        index === 3 ? 55 :
-        index === 4 ? 46 :
-        index === 5 ? 51 :
-        index === 6 ? 55 :
-        50;
+      const standardDeviationRange = calculateRangeStdDev(
+        meanLaunchSpeed,
+        standardDeviationSpeed,
+        30,
+        standardDeviationAngle
+      );
+      console.log( standardDeviationRange );
 
-      const range = new Range( thumbnailMean - thumbnailDomain / 2, thumbnailMean + thumbnailDomain / 2 );
+      const SCALE_STANDARD_DEVIATION = 3;
+      const addedPadding = binWidth; // Show at least one bin on either side so the bars don't go offscreen too much
+      const range = new Range( meanRange - SCALE_STANDARD_DEVIATION * standardDeviationRange - addedPadding, meanRange + SCALE_STANDARD_DEVIATION * standardDeviationRange + addedPadding );
+      console.log( range );
 
       chartTransform.setModelXRange( range );
     } );
Index: js/common/view/HistogramNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/view/HistogramNode.ts b/js/common/view/HistogramNode.ts
--- a/js/common/view/HistogramNode.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/view/HistogramNode.ts	(date 1713801041223)
@@ -86,7 +86,7 @@
       // Tuned to make the spacing between the right panel and the histogram match that of the left
       viewWidth: 561,
       viewHeight: 165,
-      modelXRange: new Range( 0, PDLConstants.MAX_FIELD_DISTANCE ),
+      modelXRange: new Range( 20, 80 ),
       modelYRange: new Range( 0, 25 )
     } );
 
Index: js/common/model/PDLModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/PDLModel.ts b/js/common/model/PDLModel.ts
--- a/js/common/model/PDLModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/PDLModel.ts	(date 1713801041211)
@@ -28,6 +28,7 @@
 import StringUnionProperty from '../../../../axon/js/StringUnionProperty.js';
 import TProperty from '../../../../axon/js/TProperty.js';
 import PhetioProperty from '../../../../axon/js/PhetioProperty.js';
+import NumberIO from '../../../../tandem/js/types/NumberIO.js';
 
 // See the documentation at the PDLModel class attributes for more information on these options.
 type SelfOptions<T extends Field> = {
@@ -78,6 +79,9 @@
   public readonly projectileTypeProperty: PhetioProperty<ProjectileType>;
 
   public readonly meanLaunchAngleProperty: TReadOnlyProperty<number>;
+  public readonly meanLaunchSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly launcherHeightProperty: TReadOnlyProperty<number>;
 
@@ -166,6 +170,24 @@
       derive: field => field.meanAngleProperty
     } );
 
+    this.meanLaunchSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.meanSpeedProperty
+    } );
+
+    this.standardDeviationSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationSpeedProperty
+    } );
+
+    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationAngleProperty,
+      tandem: Tandem.OPT_OUT, // TODO: providedOptions.isStandardDeviationAnglePropertyPhetioInstrumented ? providedOptions.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
+      phetioFeatured: true,
+      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
+      phetioValueType: NumberIO,
+      phetioReadOnly: true,
+      phetioState: false
+    } );
+
     this.launcherHeightProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,
       derive: field => field.launchHeightProperty
Index: js/sampling/view/SamplingAccordionBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingAccordionBox.ts b/js/sampling/view/SamplingAccordionBox.ts
--- a/js/sampling/view/SamplingAccordionBox.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingAccordionBox.ts	(date 1713801041251)
@@ -24,6 +24,7 @@
 import Launcher from '../../common/model/Launcher.js';
 import NumberProperty from '../../../../axon/js/NumberProperty.js';
 import Histogram from '../../common/model/Histogram.js';
+import SamplingModel from '../model/SamplingModel.js';
 
 type SelfOptions = EmptySelfOptions;
 
@@ -33,6 +34,7 @@
 export default class SamplingAccordionBox extends HistogramAccordionBox {
 
   public constructor(
+    model: SamplingModel,
     histogram: Histogram,
     launcherProperty: TReadOnlyProperty<Launcher>,
     sampleSizeProperty: TReadOnlyProperty<number>,
@@ -71,22 +73,22 @@
       tandem: thumbnailContainerTandem,
       visiblePropertyOptions: { phetioFeatured: true },
       children: [
-        new SampleSizeThumbnailNode( 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize2ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize5ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize15ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sample40ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
Index: js/common/model/Field.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/Field.ts b/js/common/model/Field.ts
--- a/js/common/model/Field.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/Field.ts	(date 1713801041198)
@@ -67,10 +67,10 @@
   // These values are DynamicProperties that are determined by the Launcher, see Launcher.ts and implementation-notes.md
 
   // Specifies the average speed of launched projectiles
-  private readonly meanSpeedProperty: TReadOnlyProperty<number>;
+  public readonly meanSpeedProperty: TReadOnlyProperty<number>;
 
   // Specifies the speed standard deviation for launched projectiles.
-  private readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
 
   // Indicates the current value for the standard deviation of the angle of launch.
   public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
Index: js/common-vsm/model/VSMModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common-vsm/model/VSMModel.ts b/js/common-vsm/model/VSMModel.ts
--- a/js/common-vsm/model/VSMModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common-vsm/model/VSMModel.ts	(date 1713801041233)
@@ -64,7 +64,6 @@
 
   // The angleStabilityProperty represents the amount of angle stabilization applied to the launcher. The value is between 0 and 1, where 0 means minimum stabilization and 1 means maximum stabilization.
   public readonly angleStabilityProperty: PhetioProperty<number>;
-  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly selectedProjectileNumberProperty: PhetioProperty<number>;
   public readonly selectedProjectileProperty: TReadOnlyProperty<Projectile | null>;
@@ -131,16 +130,6 @@
       phetioReadOnly: true,
       phetioState: false
     } );
-
-    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
-      derive: field => field.standardDeviationAngleProperty,
-      tandem: options.isStandardDeviationAnglePropertyPhetioInstrumented ? options.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
-      phetioFeatured: true,
-      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
-      phetioValueType: NumberIO,
-      phetioReadOnly: true,
-      phetioState: false
-    } );
 
     this.selectedProjectileNumberProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,

@samreid
Copy link
Member Author

samreid commented Apr 22, 2024

Add an option for whether the dynamic property is instrumented:

Subject: [PATCH] Vertically flip boat and bottle icon images, see https://github.com/phetsims/buoyancy/issues/141
---
Index: js/sampling/view/SamplingScreenView.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingScreenView.ts b/js/sampling/view/SamplingScreenView.ts
--- a/js/sampling/view/SamplingScreenView.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingScreenView.ts	(date 1713801041259)
@@ -155,6 +155,7 @@
       } );
 
     this.accordionBox = new SamplingAccordionBox(
+      model,
       model.histogram,
       model.launcherProperty,
       model.sampleSizeProperty,
Index: js/sampling/view/SampleSizeThumbnailNode.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SampleSizeThumbnailNode.ts b/js/sampling/view/SampleSizeThumbnailNode.ts
--- a/js/sampling/view/SampleSizeThumbnailNode.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SampleSizeThumbnailNode.ts	(date 1713801857732)
@@ -28,12 +28,15 @@
 import { ZOOM_LEVELS } from '../../common/model/Histogram.js';
 import WithRequired from '../../../../phet-core/js/types/WithRequired.js';
 import { EmptySelfOptions } from '../../../../phet-core/js/optionize.js';
+import SamplingModel from '../model/SamplingModel.js';
+import Multilink from '../../../../axon/js/Multilink.js';
 
 type SelfOptions = EmptySelfOptions;
 type SampleSizeThumbnailNodeOptions = SelfOptions & WithRequired<NodeOptions, 'tandem'>;
 
 export default class SampleSizeThumbnailNode extends Node {
-  public constructor( thumbnailSampleSize: number,
+  public constructor( model: SamplingModel,
+                      thumbnailSampleSize: number,
                       fieldProperty: TReadOnlyProperty<SamplingField>,
                       fields: SamplingField[],
                       binWidthProperty: TReadOnlyProperty<number>,
@@ -51,32 +54,49 @@
       modelYRange: new Range( 0, 10 )
     } );
 
+    function calculateRangeStdDev( meanSpeed: number, stdDevSpeed: number, meanAngle: number, stdDevAngle: number ): number {
+      const g = 9.81; // acceleration due to gravity in m/s^2
+      const radiansMeanAngle = meanAngle * Math.PI / 180; // convert mean angle from degrees to radians
+      const radiansStdDevAngle = stdDevAngle * Math.PI / 180; // convert standard deviation angle from degrees to radians
+
+      // Partial derivatives evaluated at mean values
+      const partialV = ( 2 * meanSpeed / g ) * Math.sin( 2 * radiansMeanAngle );
+      const partialTheta = ( 2 * meanSpeed ** 2 / g ) * Math.cos( 2 * radiansMeanAngle );
+
+      // Variance components
+      const varV = partialV ** 2 * stdDevSpeed ** 2;
+      const varTheta = partialTheta ** 2 * radiansStdDevAngle ** 2;
+
+      // Total variance of the range
+      const totalVariance = varV + varTheta;
+
+      // Standard deviation of the range
+      const rangeStdDev = Math.sqrt( totalVariance );
+
+      return rangeStdDev;
+    }
+
     // Horizontally zoom in on the thumbnails, centering on the average output for the mystery launcher
     // Each launcher has a different average output, so we need to adjust the range for the thumbnail histogram based on the mystery launcher
-    fieldProperty.link( field => {
 
-      const index = field.launcherProperty.value.launcherNumber;
+    Multilink.multilink( [ model.histogram.binWidthProperty, model.meanLaunchSpeedProperty, model.standardDeviationSpeedProperty, model.standardDeviationAngleProperty ], ( binWidth, meanLaunchSpeed, standardDeviationSpeed,
+                                                                                                                                                                            standardDeviationAngle ) => {
 
-      // The width of the range of the thumbnail histogram
-      const thumbnailDomain =
-        index === 1 ? 20 :
-        index === 2 ? 40 :
-        index === 3 ? 30 :
-        index === 4 ? 19 :
-        index === 5 ? 20 :
-        index === 6 ? 50 :
-        40;
+      const g = 9.8; // TODO: Use same constant
+      const meanRange = Math.pow( meanLaunchSpeed, 2 ) / g * Math.sin( 2 * 30 * Math.PI / 180 );
 
-      const thumbnailMean =
-        index === 1 ? 46 :
-        index === 2 ? 50 :
-        index === 3 ? 55 :
-        index === 4 ? 46 :
-        index === 5 ? 51 :
-        index === 6 ? 55 :
-        50;
+      const standardDeviationRange = calculateRangeStdDev(
+        meanLaunchSpeed,
+        standardDeviationSpeed,
+        30,
+        standardDeviationAngle
+      );
+      console.log( standardDeviationRange );
 
-      const range = new Range( thumbnailMean - thumbnailDomain / 2, thumbnailMean + thumbnailDomain / 2 );
+      const SCALE_STANDARD_DEVIATION = 3;
+      const addedPadding = binWidth; // Show at least one bin on either side so the bars don't go offscreen too much
+      const range = new Range( meanRange - SCALE_STANDARD_DEVIATION * standardDeviationRange - addedPadding, meanRange + SCALE_STANDARD_DEVIATION * standardDeviationRange + addedPadding );
+      console.log( range );
 
       chartTransform.setModelXRange( range );
     } );
Index: js/sampling/model/SamplingModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/model/SamplingModel.ts b/js/sampling/model/SamplingModel.ts
--- a/js/sampling/model/SamplingModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/model/SamplingModel.ts	(date 1713803066749)
@@ -34,7 +34,7 @@
 
 type SelfOptions = EmptySelfOptions;
 
-type SamplingModelOptions = SelfOptions & StrictOmit<PDLModelOptions<SamplingField>, 'timeSpeedValues' | 'fields' | 'isPathsVisible' | 'isFieldPropertyPhetioReadonly' | 'fieldPropertyPhetioDocumentation' | 'isPathVisibilityPhetioInstrumented'>;
+type SamplingModelOptions = SelfOptions & StrictOmit<PDLModelOptions<SamplingField>, 'timeSpeedValues' | 'fields' | 'isPathsVisible' | 'isFieldPropertyPhetioReadonly' | 'fieldPropertyPhetioDocumentation' | 'isPathVisibilityPhetioInstrumented' | 'isStandardDeviationAnglePropertyPhetioInstrumented'>;
 
 export const SAMPLE_SIZES = [ 2, 5, 15, 40 ];
 
@@ -85,7 +85,8 @@
       isPathVisibilityPhetioInstrumented: false,
       isFieldPropertyPhetioReadonly: true,
       fieldPropertyPhetioDocumentation: 'This Property represents the field that is currently selected. '
-                                        + 'On the Sampling screen, each combination of launcher and sample size has its own field.'
+                                        + 'On the Sampling screen, each combination of launcher and sample size has its own field.',
+      isStandardDeviationAnglePropertyPhetioInstrumented: false
     }, providedOptions );
 
     super( options );
Index: js/common/model/PDLModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/PDLModel.ts b/js/common/model/PDLModel.ts
--- a/js/common/model/PDLModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/PDLModel.ts	(date 1713803014519)
@@ -28,6 +28,7 @@
 import StringUnionProperty from '../../../../axon/js/StringUnionProperty.js';
 import TProperty from '../../../../axon/js/TProperty.js';
 import PhetioProperty from '../../../../axon/js/PhetioProperty.js';
+import NumberIO from '../../../../tandem/js/types/NumberIO.js';
 
 // See the documentation at the PDLModel class attributes for more information on these options.
 type SelfOptions<T extends Field> = {
@@ -39,6 +40,8 @@
   isPathVisibilityPhetioInstrumented: boolean;
   isFieldPropertyPhetioReadonly: boolean;
   fieldPropertyPhetioDocumentation: string;
+
+  isStandardDeviationAnglePropertyPhetioInstrumented: boolean;
 };
 export type PDLModelOptions<T extends Field> = SelfOptions<T> & { tandem: Tandem };
 
@@ -78,6 +81,9 @@
   public readonly projectileTypeProperty: PhetioProperty<ProjectileType>;
 
   public readonly meanLaunchAngleProperty: TReadOnlyProperty<number>;
+  public readonly meanLaunchSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly launcherHeightProperty: TReadOnlyProperty<number>;
 
@@ -166,6 +172,24 @@
       derive: field => field.meanAngleProperty
     } );
 
+    this.meanLaunchSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.meanSpeedProperty
+    } );
+
+    this.standardDeviationSpeedProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationSpeedProperty
+    } );
+
+    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
+      derive: field => field.standardDeviationAngleProperty,
+      tandem: providedOptions.isStandardDeviationAnglePropertyPhetioInstrumented ? providedOptions.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
+      phetioFeatured: true,
+      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
+      phetioValueType: NumberIO,
+      phetioReadOnly: true,
+      phetioState: false
+    } );
+
     this.launcherHeightProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,
       derive: field => field.launchHeightProperty
Index: js/sampling/view/SamplingAccordionBox.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/sampling/view/SamplingAccordionBox.ts b/js/sampling/view/SamplingAccordionBox.ts
--- a/js/sampling/view/SamplingAccordionBox.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/sampling/view/SamplingAccordionBox.ts	(date 1713801041251)
@@ -24,6 +24,7 @@
 import Launcher from '../../common/model/Launcher.js';
 import NumberProperty from '../../../../axon/js/NumberProperty.js';
 import Histogram from '../../common/model/Histogram.js';
+import SamplingModel from '../model/SamplingModel.js';
 
 type SelfOptions = EmptySelfOptions;
 
@@ -33,6 +34,7 @@
 export default class SamplingAccordionBox extends HistogramAccordionBox {
 
   public constructor(
+    model: SamplingModel,
     histogram: Histogram,
     launcherProperty: TReadOnlyProperty<Launcher>,
     sampleSizeProperty: TReadOnlyProperty<number>,
@@ -71,22 +73,22 @@
       tandem: thumbnailContainerTandem,
       visiblePropertyOptions: { phetioFeatured: true },
       children: [
-        new SampleSizeThumbnailNode( 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 2, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize2ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 5, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize5ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 15, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sampleSize15ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
           } ),
-        new SampleSizeThumbnailNode( 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
+        new SampleSizeThumbnailNode( model, 40, fieldProperty, fields, binWidthProperty, histogramRepresentationProperty,
           PDLColors.meanMarkerFillProperty, PDLColors.meanMarkerStrokeProperty, zoomProperty, {
             tandem: thumbnailContainerTandem.createTandem( 'sample40ThumbnailNode' ),
             visiblePropertyOptions: { phetioFeatured: true }
Index: js/common/model/Field.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common/model/Field.ts b/js/common/model/Field.ts
--- a/js/common/model/Field.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common/model/Field.ts	(date 1713801041198)
@@ -67,10 +67,10 @@
   // These values are DynamicProperties that are determined by the Launcher, see Launcher.ts and implementation-notes.md
 
   // Specifies the average speed of launched projectiles
-  private readonly meanSpeedProperty: TReadOnlyProperty<number>;
+  public readonly meanSpeedProperty: TReadOnlyProperty<number>;
 
   // Specifies the speed standard deviation for launched projectiles.
-  private readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
+  public readonly standardDeviationSpeedProperty: TReadOnlyProperty<number>;
 
   // Indicates the current value for the standard deviation of the angle of launch.
   public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
Index: js/common-vsm/model/VSMModel.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/js/common-vsm/model/VSMModel.ts b/js/common-vsm/model/VSMModel.ts
--- a/js/common-vsm/model/VSMModel.ts	(revision 2851e941e118bccd98755027f88d98d25cb03de5)
+++ b/js/common-vsm/model/VSMModel.ts	(date 1713801041233)
@@ -64,7 +64,6 @@
 
   // The angleStabilityProperty represents the amount of angle stabilization applied to the launcher. The value is between 0 and 1, where 0 means minimum stabilization and 1 means maximum stabilization.
   public readonly angleStabilityProperty: PhetioProperty<number>;
-  public readonly standardDeviationAngleProperty: TReadOnlyProperty<number>;
 
   public readonly selectedProjectileNumberProperty: PhetioProperty<number>;
   public readonly selectedProjectileProperty: TReadOnlyProperty<Projectile | null>;
@@ -131,16 +130,6 @@
       phetioReadOnly: true,
       phetioState: false
     } );
-
-    this.standardDeviationAngleProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
-      derive: field => field.standardDeviationAngleProperty,
-      tandem: options.isStandardDeviationAnglePropertyPhetioInstrumented ? options.tandem.createTandem( 'standardDeviationAngleProperty' ) : Tandem.OPT_OUT,
-      phetioFeatured: true,
-      phetioDocumentation: 'This Property represents the standard deviation of the angle of launch.',
-      phetioValueType: NumberIO,
-      phetioReadOnly: true,
-      phetioState: false
-    } );
 
     this.selectedProjectileNumberProperty = new DynamicProperty<number, number, T>( this.fieldProperty, {
       bidirectional: true,

samreid added a commit that referenced this issue Apr 22, 2024
…n using propagation of error based on the launcher properties, see #301
matthew-blackman pushed a commit that referenced this issue Apr 22, 2024
…n using propagation of error based on the launcher properties, see #301

(cherry picked from commit 54ab1ab)
@matthew-blackman
Copy link
Contributor

Please close after verifying.

@KatieWoe
Copy link

If you launch a data set, then change the launcher type the data in the thumbnail will be off center until new data is created.

PhET-iO.Studio.-.Google.Chrome.2024-04-29.13-14-42.mp4

@matthew-blackman
Copy link
Contributor

@KatieWoe that's correct, we discussed that and noted it in the documentation as:

Launcher characteristics should be set before generating data (otherwise some data may fall out of bounds of the thumbnails).

We felt that this is reasonable, and the case of updating the launcher properties after collecting data from a previous value fell outside the intended use of PhET-IO customization. This issue can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants