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

Light view should return to black. #258

Closed
samreid opened this issue Dec 17, 2018 · 16 comments
Closed

Light view should return to black. #258

samreid opened this issue Dec 17, 2018 · 16 comments

Comments

@samreid
Copy link
Member

samreid commented Dec 17, 2018

In #251 (comment) @ariel-phet said:

@samreid there is definitely something odd here. I am not so disturbed by there being some ripples, but this behavior is not the same as Java.

What is actually more important in my opinion is that if you start off on "pulse" and then send a single pulse out, the entire background is eventually green. That seems pedagogically just wrong, it is like the space is just all filled with green light. Again, Java does not have this behavior. It is as if the lattice is not getting restored to black at all.

Java also has the same general idea of mapping -- when the wave is running, "black" represents a trough, but when the wave is shut off, black represents zero. In Java, when the wave is shut off or a single pulse is given it seems to send a "make the lattice black wave" out behind the pulse. It would be nice to match this behavior....and it looks like the Java model has some specific logic to restore the lattice to black.

@samreid when the pulse is used or the continuous wave is turned off, couldn't there be some wave sent out that is sort of invisible to the user that clears the lattice and puts in to the default starting condition? That seems to be what Java is doing?

@samreid samreid self-assigned this Dec 17, 2018
@arouinfar
Copy link
Contributor

This has also bugged me @samreid, but I didn't realize the Java version reset the lattice to black.

@samreid
Copy link
Member Author

samreid commented Dec 17, 2018

The pulse rendering in Java looks like this, and the wave disappears before it reaches the right hand side of the lattice:

image

Is this acceptable?

Should I pursue a similar algorithm as in Java? An alternative would be to use a heuristic to determine when the time average of the wave in a cell had gone below a threshold, then to turn it to black. Either way, I think we will see artifacts.

@ariel-phet marked this as "priority:2-high", so I'm presuming this must be addressed before 1.0, is that correct?

@ariel-phet
Copy link

@samreid yes, I think this issue must be addressed in a reasonable way before 1.0 is published. The ending before the right side of the lattice should be avoided if possible. I think I would see how the clearing of the continuous wave is done, and follow that approach if possible. Some artifacts will be acceptable, it just depends on the level of severity.

@arouinfar
Copy link
Contributor

The pulse rendering in Java looks like this, and the wave disappears before it reaches the right hand side of the lattice:

That looks pretty bad to me, but I agree with @ariel-phet. Let's address the continuous wave first.

Should I pursue a similar algorithm as in Java? An alternative would be to use a heuristic to determine when the time average of the wave in a cell had gone below a threshold, then to turn it to black. Either way, I think we will see artifacts.

The Java algorithm seems like a reasonable place to start to me @samreid. I think the other approach could get messy. What about a low-amplitude wave far from the source? We would need a reasonable threshold that didn't allow any cells to turn black unless the source was truly off.

@samreid
Copy link
Member Author

samreid commented Jan 2, 2019

The strategy used in Java is asymmetrical--note that the dark wave only emits from one of the disabled lights:

image

Also, I noticed it is not perfectly circular--not sure why.

@samreid
Copy link
Member Author

samreid commented Jan 2, 2019

Note that in the "Slits" screen, when the wave is turned off, the light wave is instantly cleared everywhere. This has the desired effect of changing the wave area to black. Can we use this strategy for clearing the light scene on the first two screens? It sounds like the implementation would be straightforward, probably less than an hour.

A strategy like the one in the Java implementation would take significantly longer to implement, lead to more complex code to maintain and would exhibit odd corner case behavior like #258 (comment) and #258 (comment)

I'll hold off working on this issue until hearing from @arouinfar and @ariel-phet.

@samreid samreid assigned ariel-phet and arouinfar and unassigned samreid Jan 2, 2019
@ariel-phet
Copy link

ariel-phet commented Jan 2, 2019

@samreid That might be a fine strategy for turning off the "continuous light". (In fact, I am totally on board with that strategy if the laser is button is turn in continuous mode on the waves screen). I am not sure what you would do on the interference screen since the buttons can be turned off independently? This strategy also does not seem to fix the problem of waves on in "pulse" mode on the waves screen. (as in, how do I send out a single pulse that does not leave a "trail" of light)

@ariel-phet ariel-phet assigned samreid and unassigned ariel-phet and arouinfar Jan 2, 2019
@samreid
Copy link
Member Author

samreid commented Jan 4, 2019

Once we solve this issue for light, it may be possible to leverage the same strategy to clear out trailing ripples for sound and water, which would address #251. Not 100% sure that will need to be done, but making a comment here in case it is helpful to keep that in mind.

@samreid
Copy link
Member Author

samreid commented Jan 5, 2019

Here's a patch that looks like this:

image

It uses the strategy from Java to propagate a parallel lattice of "dark" values and can also be used to signify shutoff values. However, there is a problem where the main wave leaks back into the shutoff wave.

image

``` Index: js/common/model/Lattice.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- js/common/model/Lattice.js (revision 1c52708) +++ js/common/model/Lattice.js (date 1546646534000) @@ -186,6 +186,10 @@ this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].set( i, j, value ); }
  • setHasCellBeenVisited( i, j, v ) {

  •  this.visitedMatrix.set( i, j, v );
    
  • }

  • /**
    * Determines whether the incoming wave has reached the cell.
    * @param {number} i - horizontal coordinate to check
    Index: js/waves/model/WavesModel.js
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    ===================================================================
    --- js/waves/model/WavesModel.js (revision 1c52708)
    +++ js/waves/model/WavesModel.js (date 1546644495000)
    @@ -234,7 +234,7 @@
    );

     // @public {Property.<Scene>} - selected scene
    
  •  this.sceneProperty = new Property( this.waterScene, {
    
  •  this.sceneProperty = new Property( this.lightScene, {
       validValues: this.scenes
     } );
    

Index: js/common/model/Scene.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8

--- js/common/model/Scene.js (revision 1c52708)
+++ js/common/model/Scene.js (date 1546647089000)
@@ -177,6 +177,14 @@
WaveInterferenceConstants.LATTICE_PADDING
);

  •  // @public - a pulse is sent out when the source is disabled--this restores zero values or, for light, the vacuum
    
  •  this.cleanupLattice = new Lattice(
    
  •    WaveInterferenceConstants.LATTICE_DIMENSION,
    
  •    WaveInterferenceConstants.LATTICE_DIMENSION,
    
  •    WaveInterferenceConstants.LATTICE_PADDING,
    
  •    WaveInterferenceConstants.LATTICE_PADDING
    
  •  );
    
  •  // @public - elapsed time in seconds
     this.timeProperty = new NumberProperty( 0 );
    

@@ -280,6 +288,13 @@
// @public - true when the first source is continuously oscillating
this.continuousWave1OscillatingProperty = new BooleanProperty( false );

  •  this.wasRecentlyShutOff = false;
    
  •  this.continuousWave1OscillatingProperty.lazyLink( continuousWave1Oscillating => {
    
  •    if ( !continuousWave1Oscillating ) {
    
  •      this.wasRecentlyShutOff = true;
    
  •    }
    
  •  } );
    
  •  // @public - true when the second source is continuously oscillating
     this.continuousWave2OscillatingProperty = new BooleanProperty( false );
    

@@ -327,6 +342,7 @@
// When the barrier moves it creates a lot of artifacts, so clear the wave right of the barrier when it moves
this.barrierLatticeCoordinateProperty.link( barrierLatticeCoordinate => {
this.lattice.clearRight( barrierLatticeCoordinate );

  •      this.cleanupLattice.clearRight( barrierLatticeCoordinate );
       } );
    
       // @private {number} - phase of the wave so it doesn't start halfway through a cycle
    

@@ -386,13 +402,14 @@
const isContinuous = ( this.disturbanceTypeProperty.get() === DisturbanceTypeEnum.CONTINUOUS );
const continuous1 = isContinuous && this.continuousWave1OscillatingProperty.get();
const continuous2 = isContinuous && this.continuousWave2OscillatingProperty.get();

  •  if ( continuous1 || continuous2 || this.pulseFiringProperty.get() ) {
    
  •  if ( continuous1 || continuous2 || this.pulseFiringProperty.get() || this.wasRecentlyShutOff ) {
    
       // The simulation is designed to start with a downward wave, corresponding to water splashing in
       const frequency = this.frequencyProperty.value;
       const angularFrequency = Math.PI * 2 * frequency;
    
       // For 50% longer than one pulse, keep the oscillator fixed at 0 to prevent "ringing"
    
  •    // TODO: may no longer be necessary after we have cleanupLattice
       const waveValue = ( this.pulseFiringProperty.get() && timeSincePulseStarted > period ) ? 0 :
                         -Math.sin( time * angularFrequency + this.phase ) * amplitude;
    

@@ -420,6 +437,12 @@
lattice.setCurrentValue( WaveInterferenceConstants.POINT_SOURCE_HORIZONTAL_COORDINATE, j, waveValue );
this.oscillator2Property.value = waveValue;
}
+

  •    if ( this.wasRecentlyShutOff ) {
    
  •      const j = latticeCenterJ + distanceFromCenter;
    
  •      this.cleanupLattice.setCurrentValue( WaveInterferenceConstants.POINT_SOURCE_HORIZONTAL_COORDINATE, j, 40 );
    
  •      this.wasRecentlyShutOff = false;
    
  •    }
     }
    
    }

@@ -561,6 +584,29 @@

   // Update the lattice
   this.lattice.step();
  •  this.cleanupLattice.step();
    
  •  // use the cleanup lattice to scrub the main lattice
    
  •  for ( let i = 0; i < this.lattice.width; i++ ) {
    
  •    for ( let j = 0; j < this.lattice.height; j++ ) {
    
  •      // damp out the cleanup wave
    
  •      const scale = 0.98;
    
  •      this.cleanupLattice.setCurrentValue( i, j, this.cleanupLattice.getCurrentValue( i, j ) * scale );
    
  •      this.cleanupLattice.setLastValue( i, j, this.cleanupLattice.getLastValue( i, j ) * scale );
    
  •      if ( Math.abs( this.cleanupLattice.getCurrentValue( i, j ) ) > 0.01 ) {
    
  •        this.lattice.setCurrentValue( i, j, 0 );
    
  •        this.lattice.setLastValue( i, j, 0 );
    
  •        this.lattice.setHasCellBeenVisited( i, j, 0 );
    
  •      }
    
  •      // Fresh waves wipe out the cleaning layer.  Hoo boy this is getting confusing and causing back-waves
    
  •      if ( Math.abs( this.lattice.getCurrentValue( i, j ) ) > 0.01 ) {
    
  •        this.cleanupLattice.setCurrentValue( i, j, 0 );
    
  •        this.cleanupLattice.setLastValue( i, j, 0 );
    
  •      }
    
  •    }
    
  •  }
    
     // Apply values on top of the computed lattice values so there is no noise at the point sources
     this.setSourceValues();
    

@@ -570,6 +616,7 @@

   // Notify listeners about changes
   this.lattice.changedEmitter.emit();
  •  this.cleanupLattice.changedEmitter.emit();
    

    }

    /**
    @@ -578,6 +625,7 @@
    */
    clear() {
    this.lattice.clear();

  •  this.cleanupLattice.clear();
    

    }

    /**

@samreid
Copy link
Member Author

samreid commented Jan 5, 2019

I think I understand the Java model better--it is creating a new lattice each time you shut off a wave. Then in has a heuristic for what constitutes a wave front, and uses that to erase values in the view only (not in the model). Another heuristic determines when to delete the new lattices.

@samreid
Copy link
Member Author

samreid commented Jan 5, 2019

I made some improvements--support for pulse, and clear out the shutoff wave. Still has significant problems though. I'll push to a branch just in case.

@samreid
Copy link
Member Author

samreid commented Jan 6, 2019

The strategy proposed in #300 is working nicely, I'll promote it to master.

@samreid
Copy link
Member Author

samreid commented Jan 6, 2019

On hold until I figure out #302, which may be related.

@samreid
Copy link
Member Author

samreid commented Jan 7, 2019

I have a workaround in place for #302, and I published https://phet-dev.colorado.edu/html/wave-interference/1.0.0-dev.57/phet/wave-interference_en_phet.html which clears the residual wave artifacts after turning off the source (whether continuous or pulse). It also significantly cleans up the graph when the wave is off. @arouinfar can you please take it for a test drive and let me know if you see any problems with it?

@arouinfar
Copy link
Contributor

In terms of the lattice clearing when the source is turned off, dev.57 seems to do the trick. However #304 is super problematic.

Not sure that this is ready to close, so kicking it back to @samreid.

@samreid
Copy link
Member Author

samreid commented Jan 10, 2019

In design meeting, this seemed good, closing.

@samreid samreid closed this as completed Jan 10, 2019
samreid added a commit that referenced this issue Jan 10, 2019
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