phetsims / scenery-phet

Reusable components based on Scenery that are specific to PhET simulations.
http://scenerystack.org/
MIT License
8 stars 6 forks source link

PhET-iO instrumentation for ColorProfile #515

Closed pixelzoom closed 3 years ago

pixelzoom commented 5 years ago

I discovered while adding tandems to Gas Properties in https://github.com/phetsims/gas-properties/issues/30 that ColorProfile is not set up for PhET-iO use. I'm not sure how important it is, that would be a question for designers. But I can imagine clients wanting to (for example) customize the color of particles in Gas Properties.

pixelzoom commented 5 years ago

Thoughts:

pixelzoom commented 5 years ago

Looks like profileNameProperty is the only bit that is currently instrumented.

zepumph commented 5 years ago

@pixelzoom I'm trying to note the priority for this. Is there a sim currently under phet-io development that is using a ColorProfile. If not please let us know when that is the case. I think that will be the easiest way to proceed here.

pixelzoom commented 5 years ago

As noted in the first comment, gas-properties uses ColorProfiles, and whether they need to be instrumented is a question for designers. If (for example) allowing PhET-iO clients to change the particle colors is a desired feature, then ColorProfiles (and the Properties that they synthesize) will need to be instrumented.

pixelzoom commented 4 years ago

This came up again in Fourier, where we want to use ColorProfile, and be able to change colors via PhET-iO. See https://github.com/phetsims/fourier-making-waves/issues/5.

zepumph commented 4 years ago

Above, I added studio support for PropertyIO<ColorIO>. It was simple enough, and felt like something we should have no matter what.

This patch could instrument every colorProperty, which could be a poor, manual approach to creating your own color profile, but it can be overridden, for example if you set some Properties, and then toggle the projector mode checkbox on and then off again, it blows away your customizations.

Index: js/ColorProfile.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/ColorProfile.js  (revision 1a81c88e54ba0f370fdce8459aafb4f1bcfea4dd)
+++ js/ColorProfile.js  (date 1604956799275)
@@ -89,6 +89,8 @@
       validValues: profileNames
     } );

+    const colorPropertiesTandem = options.tandem.createTandem( 'colorProperties' );
+
     Object.keys( colors ).sort().forEach( key => {
       if ( colors.hasOwnProperty( key ) ) {

@@ -103,9 +105,14 @@
         // Use the requested initial profile, fallback to default.
         const initialColor = colorMap[ initialProfileName ] || colorMap[ ColorProfile.DEFAULT_COLOR_PROFILE_NAME ];

+        const colorPropertyName = key + 'Property';
+
         // Create a Property for the color
-        const colorProperty = new Property( initialColor );
-        this[ key + 'Property' ] = colorProperty;
+        const colorProperty = new Property( initialColor, {
+          tandem: colorPropertiesTandem.createTandem( colorPropertyName ),
+          phetioType: Property.PropertyIO( Color.ColorIO )
+        } );
+        this[ colorPropertyName ] = colorProperty;

         // Update the Property on profile name changes
         this.profileNameProperty.lazyLink( profileName => {

I'm not really sure if this is something to look into further, or if we should start here. Likely things should be more designed. In the mean time, @samreid will you check on my commits, and recommend a step forward for this issue as you see fit.

samreid commented 4 years ago

I tested capacitorLabBasics.capacitanceScreen.model.arrowColorProperty in studio, and it looks really great, nice work @zepumph. I think for many sims, changing the color profile programmatically will be OK. For Fourier's amplitude colors, @ariel-phet said:

I do not even think these colors should be "easily edited" with iO for the same reasons. That seems like a "bell and whistle" that is potentially problematic.

I recommend we wait until we have a concrete use case before working on this further.

zepumph commented 4 years ago

My commits from https://github.com/phetsims/scenery-phet/issues/515#issuecomment-724300804 have brought up discussion in https://github.com/phetsims/fourier-making-waves/issues/13#issuecomment-726923339 and https://github.com/phetsims/scenery/issues/1115.

zepumph commented 4 years ago

@samreid said that this desire was brought up again in a design meeting yesterday, we spoke about how we may accomplish this and thought about the following:

  1. Instrument all Color Properties in ColorProfile as phetioReadOnly.
  2. Create ColorProfileIO, which has a studio HTML for customizing the Profile colors, not just the Property values. This would likely mean an IOType method, like setColorForProfile that manipulated the underlying color map (https://github.com/phetsims/scenery-phet/blob/e4ec40deb7170cbb08ee20769f7eb09e469665fa/js/ColorProfile.js#L96).
  3. Provide a way that ColorProfile can be phetioReadyOnly, but that won't effect the usability of colorProfileProperty (which is never read-only). In https://github.com/phetsims/fourier-making-waves/issues/5#issuecomment-726388930 it sounded like this was important for Fourier Making Waves.
pixelzoom commented 4 years ago

If we got the route of something like ColorProfileIO, let's make sure that we have the ability to exclude some of a profile's color Properties from instrumentation. The set of colors that we put in ColorProfile so that PhET designers can experiment with colors is not necessarily the same set that we want to present to PhET-iO designers in Studio.

EDIT: Or discuss this with PhET designers, to see whether "all or nothing" (always instrumenting the complete set of Properties in a ColorProfile) is acceptable.

jonathanolson commented 3 years ago

I won't be able to serialize Density/Buoyancy without ReferenceIO-style references to ColorProfile Properties. Each Material can take a general Property.<Color> for its solid or liquid appearance, and many times these are ColorProfile Property references. Elevating priority.

samreid commented 3 years ago

ColorProfile suffers from the same problems that PropertySet did, and we should eliminate it for the same reasons. PropertySet was discussed in https://github.com/phetsims/axon/issues/71, https://github.com/phetsims/axon/issues/101 and https://github.com/phetsims/axon/issues/102. To use the same pattern for ColorProfile, we could create a new supportive type that gives a convenient syntax for specifying Colors:

class SelectionProperty extends DerivedProperty {
  constructor( profileNameProperty, map, options ) {
    super( [ profileNameProperty ], profileName => map[ profileName ], options );
  }
}

ColorProfile could be implemented like so (untested draft):

class MyColorProfile {

  constructor( profileNames ) {
    this.profileNameProperty = new StringProperty( 'default', { validValues: profileNames } );
    this.map = {};

    window.addEventListener( 'message', event => {
      let data;
      try {
        data = JSON.parse( event.data );
      }
      catch( e ) { }

      if ( data && data.type === 'setColor' ) {
        this.map[ data.name ].value = new Color( data.value );
      }
    } );
  }

  addProperties( object ) {
    Object.keys( object ).forEach( key => {
      const property = object[ key ];
      property.link( () => this.reportColor( key, property.value ) );
      this.map[ key ] = property;
    } );
  }

  reportColor( key, color ) {
    ( window.parent !== window ) && window.parent.postMessage( JSON.stringify( {
      type: 'reportColor',
      name: key,
      value: color.toHexString()
    } ), '*' );
  }
}

And usage sites would look like this:

class MassesAndSpringsColorProfile extends MyColorProfile {

  constructor() {
    super( [ 'default', 'basics' ] );
    this.backgroundProperty = new SelectionProperty( this.profileNameProperty, {
      default: Color.white,
      basics: new Color( 'rgb( 255, 250, 227 )' )
    } )
    this.smallMysteryMassProperty = new SelectionProperty( this.profileNameProperty, {
      default: new Color( 'rgb( 246, 164, 255 )' ),
      basics: new Color( 'rgb( 185, 0, 38 )' )
    } );

    super.addProperties( {
      background: this.backgroundProperty,
      smallMysteryMass: this.smallMysteryMassProperty
    } );
  }
}

This would enable us to provide Property-specific PhET-iO instrumentation in our idiomatic way.

@jonathanolson and @pixelzoom can you please review and advise?

jonathanolson commented 3 years ago

Seems significantly more verbose, what do you see us gaining from this style? Presumably we'd need to add in tandems and phetio types manually?

samreid commented 3 years ago

PropertySet was less verbose than the alternative, but we decided against it anyways because we valued other aspects over succinctness. Using the synthesized Properties makes it look like things are broken, and fails navigation/autocompletion and highlighting:

image

As mentioned in the comment above, we have the following constraint for phet-io:

If we got the route of something like ColorProfileIO, let's make sure that we have the ability to exclude some of a profile's color Properties from instrumentation.

Creating Property instances allows us to specify phet-io instrumentation individually in the conventional way, without creating a new options syntax or equivalent.

Maybe there is a way to make the proposal a little less verbose, while still retaining the main advantages?

jonathanolson commented 3 years ago

Maybe there is a way to make the proposal a little less verbose, while still retaining the main advantages?

Perhaps a custom ColorProfileProperty? But yes this seems like a generally decent approach given that flexibility and IDE constraints.

samreid commented 3 years ago

A good next step would be to get feedback on the proposal from @pixelzoom and @zepumph.

zepumph commented 3 years ago

When I started at PhET, there was almost zero IDE and navigation support, and because of that I have a hard time ranking the importance of that. @samreid, what would be the ideal pattern here if we used TypeScript? Would anything be different?

In general I feel like we should continue with the path outlined in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-726981907. @samreid can you please note if your opinion is represented by https://github.com/phetsims/scenery-phet/issues/515#issuecomment-870599230, or if you were just brainstorming? If you would like to proceed in that direction, would you please note the primary reasons why that much of an overhaul is worth the cost?

This is common code, so lets elevate to blocking publication in general.

samreid commented 3 years ago

https://github.com/phetsims/scenery-phet/issues/515#issuecomment-726981907 does not support the features requested in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-512055519 or https://github.com/phetsims/scenery-phet/issues/515#issuecomment-726983363, but https://github.com/phetsims/scenery-phet/issues/515#issuecomment-870599230 supports it in the standard way.

Our team voted overwhelmingly in favor of abandoning PropertySet. ColorProperty has the same problems because it uses the same implementation style--I suspect the only reason we tolerate it more is because it is used less.

pixelzoom commented 3 years ago

For context, a reminder that the PhET team decided that all new sims should have a ColorProfile - see https://github.com/phetsims/scenery-phet/issues/642.

@samreid said:

... Using the synthesized Properties makes it look like things are broken, and fails navigation/autocompletion and highlighting:

This is indeed annoying. It will become more annoying as use of ColorProfile increases. It's certainly been annoying in the last few sims that I've implemented, which had ColorProfile.

... I suspect the only reason we tolerate it more is because it is used less.

I suspect that you are correct. Again, I suspect this will change as use of ColorProfile increases.

zepumph commented 3 years ago

The ability to conditionally instrument individual Properties and or to supply supplemental, and custom metadata to each Property could be solved in either way. Really this issue comes down to the pros and cons of dependency injection, especially as it pertains to PhET-iO.

We have used both patterns in the project, either supplying metadata in the form of an options object to a container to create elements (RectangularRadioButtonGroup), or to just create them and pass them in (ComboBoxItem). It's not obvious to me that either is a winner.

samreid commented 3 years ago

Can you elaborate on "dependency injection" in this context?

zepumph commented 3 years ago

Whether or not ColorProfile is instantiating the Property, or if you are, and then passing them to ColorProfile.

samreid commented 3 years ago

Should we mark this as a dev meeting subgroup so we can make a decision on this?

jessegreenberg commented 3 years ago

Discussed during 7/8/21 Developer meeting:

It was agreed during the meeting that the approach in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-870599230 is a good way forward. Everyone agreed that for improved IDE support and the ability to instrument/customize individual Properties it is worth the effort.

Next steps are for a developer to investigate and implement more thoroughly, @samreid volunteered to be assigned.

jessegreenberg commented 3 years ago

@samreid also proposed a different solution, where colors can live at their usage sites. But they get an instrumentation to support customization through the "development colors html page". This would also mean that the color would exist in the PhET-iO tree for in the components that own them, much like other instrumented view Properties. Overall, there was a question about what having an explicit ColorProfile bought for us.

Next steps are to discuss this with a design team to decide what exactly we want for customizable colors and how much time we should invest in changing ColorProfile.

jessegreenberg commented 3 years ago

Marking for phet-io meeting because the changes suggested in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-876655914 will have some impact on the PhET-iO API for colors, and there are things here that we want to discuss with design team.

samreid commented 3 years ago

I also saw a paper trail starting at https://github.com/phetsims/scenery-phet/issues/156#issuecomment-145726828 which is what led to ColorProfile originally.

zepumph commented 3 years ago

From PhET-iO meeting today:

PhET-iO designers are fine with us removing ColorProfiles, and moving color Properties into the sim. In general designers feel like the PhET-iO instrumentation level of colors will be limited (depending on client desires).

We should make color profile a singleton, and we should add support for having common code support. The singleton should live in scenery, and can be imported where ever a color is needed.

KP: We need to make sure that we are thinking about high contrast mode as we implement this.

We will keep the name "colorProfile" as the singleton

Any color can register and profile name to colorProfile, and then it is up to the sim to set only the profiles that it cares about.

This work is not blocking any soon-to-come sims, because no instrumentation of ColorProfile will change.

jbphet commented 3 years ago

@samreid has offered to do the initial work on this (thanks!), and will take a shot at doing the work in the individual simulations as well. This plan can change if it's taking too much time.

UPDATE from @samreid: @pixelzoom mentioned that it may be more efficient to do his own sims, after a pattern is established. I'll evaluate when further along.

samreid commented 3 years ago

I asked @arouinfar in slack:

For Gravity and Orbits, I noticed about 10 colors that were all white by default, and black for “projector”. Is it OK to coalesce many or all of these into a single “foreground color” property?

She replied:

Sure

samreid commented 3 years ago

Summary of changes in the upcoming commit:

The new common code

Gravity and Orbits

Next steps:

samreid commented 3 years ago

I also notified about current status on slack.

samreid commented 3 years ago

CT is showing this error: Uncaught Error: Assertion failed: colorProfile must have a profile named undefined. I'll take a look.

samreid commented 3 years ago

I pushed fixes for the current column of CT issues and will keep an eye on the next column.

samreid commented 3 years ago

Many colors were moved to their usage sites. Colors used in many different places were moved to GravityAndOrbitsConstants.js

There is a problem with this. For instance, assume there are 2 screens, Screen A and Screen B. If both screens are supposed to always have the same background color, they should use the same ColorProperty. However, if the ColorProperty is defined in the Screen constructor, they will have different ColorProperties. Defining it locally in the file as a class-static would work, but not a good match for phet-io, which does not track static tandems. So many of the ColorProperty instances we have seen should remain factored out, and cannot be moved to individual files. On the other hand, if we want the screens backgrounds to vary independently, then it is OK to define the ColorProperty locally. Maybe as a first pass, we will just leave most or all things in the ColorProfile files.

Defining it locally in the file as a class-static would work, but not a good match for phet-io, which does not track static tandems.

But we could come up with a convention for this, if it is otherwise the best plan.

samreid commented 3 years ago

Marking as blocks publication while sims are using different strategies.

samreid commented 3 years ago

ProportionPlaygroundColorProfile history has inadvertently deleted, so I restored it through reverts.

samreid commented 3 years ago

Summarizing status since this issue is getting long. The main changes are described in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-881909812, beyond that there has been a naming improvement and simplification for singleton pattern. The problem in https://github.com/phetsims/scenery-phet/issues/515#issuecomment-881955316 had me rethinking this whole issue since colors are usually per-type instead of per-instance, but I decided to proceed anyways since using the new pattern unlocks PhET-iO instrumentation and IDE support in a nice way even though there is added boilerplate. UPDATE: We can additionally scatter the colors to separate files as long as we set up a convention for the tandems (noting that the tandem tree would look a lot like it does right now, since tandems are instance based not class/static-based).

Finally, I left the @pixelzoom sims for last, considering whether I should take @pixelzoom's offer that it may be equally or more efficient for @pixelzoom to work on those, but there are some finalization steps I cannot work on until all cases are addressed, so I will do my best on those cases for now.

One stylistic question is about filename casing: according to our code review document, singletons should be lowercased, like gravityAndOrbitsColorProfile and I have updated many of them. However, I didn't know if there would be pushback about lowercasing things like FMWColorProfile to fmwColorProfile. So for that class of file, I'll leave the case alone until I have guidance about that.

samreid commented 3 years ago

Likewise, for GasProperties, there are numerous files breaking the singleton lowercase naming convention, so I'll leave those up to @pixelzoom to determine how to proceed.

pixelzoom commented 3 years ago

One stylistic question is about filename casing: according to our code review document, singletons should be lowercased, like gravityAndOrbitsColorProfile and I have updated many of them.

I recall that our decision was to have a single global ColorProfile, managed by scenery, that all repos (sims and common-code) will register with. I'm guessing it will be phet.scenery.colorProfile or something similar. That would make this issue of names like gravityAndOrbitsColorProfile moot.

Is that still the plan?

EDIT: This also becomes unnecesary with the global ColorProfile:

package.json now supports colorProfiles which is an array of strings that says which color profiles are supported in the sim.

samreid commented 3 years ago

Scenery defines colorProfileProperty but each sim still needs a place to specify sim-specific colors. For instance, see GasPropertiesColorProfile in master.

zepumph commented 3 years ago

Could we call it instead GasPropertiesColors?

samreid commented 3 years ago

Could we call it instead GasPropertiesColors?

Whether we call it GasPropertiesColors or GasPropertiesColorScheme or GasPropertiesColorProfile seems approximately equally good to me. I'm happy with GasPropertiesColorProfile because that is the term used elsewhere to indicate the colors are selected from among profiles, like ProfileColorProperty. I don't think it would be confusing for GasPropertiesColorProfile to contain other color-related things other than ProfileColorProperty instances.

pixelzoom commented 3 years ago

@samreid and I discussed on Zoom, he brought me up to speed, looks nice.

Re GasPropertiesColorProfile vis GasPropertiesColors... The former perpetuates a problem that ColorProfile had -- it's not a single profile, it's a description of 1 or more profiles (plural). Using something other than "color profile" would allow us to ditch that baggage.

zepumph commented 3 years ago

Isn't the goal/dream of this issue that modern sims would have have this culminating file of colors, and that instead they would be integrated into the usage sites, or put into CONSTANTS files if needed? So perhaps this is a bit of a moot point? I'm really fine with anything and any pattern as long as we don't suffix with ColorProfile because these are not ColorProfile instances. We only have one of those, in scenery.

pixelzoom commented 3 years ago

I'm really fine with anything and any pattern as long as we don't suffix with ColorProfile because these are not ColorProfile instances. We only have one of those, in scenery.

We don't even have 1 ColorProfile instance in scenery. We have colorProfileProperty.js, which is a StringProperty that every ProfileColorProperty observes. My understanding is that ColorProfile will be going away.

samreid commented 3 years ago

Yes, I'll be deleting ColorProfile soon.

samreid commented 3 years ago

A reminder of summary comment https://github.com/phetsims/scenery-phet/issues/515#issuecomment-882518280

pixelzoom commented 3 years ago

Reminder to:

samreid commented 3 years ago

I've completed everything for this issue or moved remaining work to side issues. This is a lengthy issue, but I reviewed most of it with @pixelzoom in a meeting this morning. I also thought it was appropriate to ask @pixelzoom to review https://github.com/phetsims/chipper/issues/1061, @pixelzoom do you also have time to review this issue? Please keep in mind a few remaining parts have been moved to side issues.

To review: