Skip to content

Commit

Permalink
Ten frame nodes can be dragged out of the icon, see #2
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisklus committed Dec 3, 2019
1 parent 5e09b3b commit 2a85a60
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 35 deletions.
5 changes: 4 additions & 1 deletion js/common/view/NumberPlayScreenView.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ define( require => {

// content for organizePlayObjectsButton
const xMargin = 4;
const tenFramePath = TenFrameNode.getTenFramePath( null, 2.5 );
const tenFramePath = TenFrameNode.getTenFramePath( {
fill: null,
lineWidth: 2.5
} );
tenFramePath.setScaleMagnitude( ( resetAllButton.width - xMargin * 2 ) / tenFramePath.width );

// create and add a button to organize the ObjectsAccordionBox playObjects in a grid
Expand Down
74 changes: 46 additions & 28 deletions js/common/view/TenFrameNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Class for a 'Ten Frame' Node, which creates ten frames (5x2 grid of squares) and fills them with dots by listening
* to the provided Property. It supports any NumberProperty with a maximum range that is a multiple of ten.
*
* The static methods can be used to draw simple ten frame nodes and provide locations for the centers of their squares.
*
* @author Chris Klusendorf (PhET Interactive Simulations)
*/
define( require => {
Expand All @@ -12,6 +14,7 @@ define( require => {
// modules
const Circle = require( 'SCENERY/nodes/Circle' );
const HBox = require( 'SCENERY/nodes/HBox' );
const merge = require( 'PHET_CORE/merge' );
const Node = require( 'SCENERY/nodes/Node' );
const numberPlay = require( 'NUMBER_PLAY/numberPlay' );
const NumberPlayConstants = require( 'NUMBER_PLAY/common/NumberPlayConstants' );
Expand Down Expand Up @@ -44,7 +47,7 @@ define( require => {

// create the calculated number of ten frames needed
_.times( numberOfTenFrames, () => {
tenFramePaths.push( TenFrameNode.getTenFramePath( 'white', LINE_WIDTH ) );
tenFramePaths.push( TenFrameNode.getTenFramePath() );
} );

// add all ten frames, aligned in a horizontal line
Expand All @@ -59,7 +62,9 @@ define( require => {
this.addChild( this.dotsLayer );

// @private {Vector2[]} - the center of every dot spot available
this.dotSpots = this.getDotSpotCenters( numberOfTenFrames );
this.dotSpots = TenFrameNode.getSpotCenters( {
numberOfTenFrames: numberOfTenFrames
} );

// update the number of dots shown whenever the current number changes
currentNumberProperty.link( currentNumber => {
Expand All @@ -84,27 +89,33 @@ define( require => {
}

/**
* Calculates the center of all dot locations for the provided number of ten frames.
* Calculates the center location of all the squares in a ten frame shape(s).
*
* @param {number} numberOfTenFrames
* @returns {Vector2[]}
* @private
* @public
*/
getDotSpotCenters( numberOfTenFrames ) {
static getSpotCenters( options ) {

options = merge( {
numberOfTenFrames: 1,
sideLength: SIDE_LENGTH,
lineWidth: LINE_WIDTH
}, options );

const spots = [];
const squareCenterOffset = SIDE_LENGTH / 2 + LINE_WIDTH / 2; // offset from the edge to the first square's center
const squareCenterOffset = options.sideLength / 2 + options.lineWidth / 2; // offset from the edge to the first square's center

// the width of i ten frames plus the space between ten frames
const nextTenFrameXOffset = NUMBER_OF_X_SQUARES * SIDE_LENGTH + DISTANCE_BETWEEN_TEN_FRAMES + LINE_WIDTH / 2;
const nextTenFrameXOffset = NUMBER_OF_X_SQUARES * options.sideLength + DISTANCE_BETWEEN_TEN_FRAMES + options.lineWidth / 2;

for ( let i = 0; i < numberOfTenFrames; i++ ) {
for ( let i = 0; i < options.numberOfTenFrames; i++ ) {
const xOffset = i * nextTenFrameXOffset; // shift over for each additional ten frame

// iterate through all squares in a ten frame and record the center of each square
for ( let j = 0; j < NUMBER_OF_Y_SQUARES; j++ ) {
const y = j * SIDE_LENGTH + squareCenterOffset;
const y = j * options.sideLength + squareCenterOffset;
for ( let k = 0; k < NUMBER_OF_X_SQUARES; k++ ) {
const x = k * SIDE_LENGTH + squareCenterOffset;
const x = k * options.sideLength + squareCenterOffset;
spots.push( new Vector2( x + xOffset, y ) );
}
}
Expand All @@ -120,35 +131,42 @@ define( require => {
* @returns {Path}
* @public
*/
static getTenFramePath( fill, lineWidth ) {
static getTenFramePath( options ) {

options = merge( {
fill: 'white',
lineWidth: LINE_WIDTH,
sideLength: SIDE_LENGTH
}, options );

const tenFrameShape = new Shape()
.moveTo( 0, 0 )

// draw the bounding rectangle, counterclockwise
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.lineToRelative( NUMBER_OF_X_SQUARES * SIDE_LENGTH, 0 )
.lineToRelative( 0, -NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.lineToRelative( -NUMBER_OF_X_SQUARES * SIDE_LENGTH, 0 )
.lineTo( 0, NUMBER_OF_Y_SQUARES / 2 * SIDE_LENGTH )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * options.sideLength )
.lineToRelative( NUMBER_OF_X_SQUARES * options.sideLength, 0 )
.lineToRelative( 0, -NUMBER_OF_Y_SQUARES * options.sideLength )
.lineToRelative( -NUMBER_OF_X_SQUARES * options.sideLength, 0 )
.lineTo( 0, NUMBER_OF_Y_SQUARES / 2 * options.sideLength )

// draw the middle horizontal line, left to right
.lineToRelative( NUMBER_OF_X_SQUARES * SIDE_LENGTH, 0 )
.lineToRelative( NUMBER_OF_X_SQUARES * options.sideLength, 0 )

// draw the inner vertical lines, right to left
.moveTo( NUMBER_OF_X_SQUARES * SIDE_LENGTH * 0.8, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.moveTo( NUMBER_OF_X_SQUARES * SIDE_LENGTH * 0.6, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.moveTo( NUMBER_OF_X_SQUARES * SIDE_LENGTH * 0.4, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.moveTo( NUMBER_OF_X_SQUARES * SIDE_LENGTH * 0.2, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * SIDE_LENGTH )
.moveTo( NUMBER_OF_X_SQUARES * options.sideLength * 0.8, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * options.sideLength )
.moveTo( NUMBER_OF_X_SQUARES * options.sideLength * 0.6, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * options.sideLength )
.moveTo( NUMBER_OF_X_SQUARES * options.sideLength * 0.4, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * options.sideLength )
.moveTo( NUMBER_OF_X_SQUARES * options.sideLength * 0.2, 0 )
.lineToRelative( 0, NUMBER_OF_Y_SQUARES * options.sideLength )
.close();

return new Path( tenFrameShape, {
fill: fill,
fill: options.fill,
stroke: 'black',
lineWidth: lineWidth
lineWidth: options.lineWidth
} );
}
}
Expand Down
15 changes: 14 additions & 1 deletion js/lab/model/LabModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ define( require => {
// @public {Array.<NumberStack>}
this.numberStacks = [];

// @public {ObservableArray.<NumberPiece>} - Number pieces in the play area (controlled or animating)
// @public {ObservableArray.<NumberPiece>} - Number pieces in the play area
this.activeNumberPieces = new ObservableArray();

// @public {ObservableArray.<TenFrame>} - ten frames in the play area
this.activeTenFrames = new ObservableArray();

// Number stacks
_.range( 1, 21 ).map( number => {
const stack = new NumberStack( number, 2, false );
Expand Down Expand Up @@ -145,6 +148,16 @@ define( require => {
}
}

/**
* Called when the user drags a ten frame from a stack.
*
* @param {TenFrame} tenFrame
* @public
*/
dragTenFrameFromIcon( tenFrame ) {
this.activeTenFrames.push( tenFrame );
}

/**
* Resets the model.
* @public
Expand Down
38 changes: 38 additions & 0 deletions js/lab/model/TenFrame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2019, University of Colorado Boulder

/**
* The model information for a DraggableTenFrameNode
*
* @author Chris Klusendorf (PhET Interactive Simulations)
*/
define( require => {
'use strict';

// modules
const numberPlay = require( 'NUMBER_PLAY/numberPlay' );
const TenFrameNode = require( 'NUMBER_PLAY/common/view/TenFrameNode' );
const Vector2Property = require( 'DOT/Vector2Property' );

class TenFrame {

/**
* @param {number} sideLength - see doc below
* @param {Vector2} initialPosition
*/
constructor( squareSideLength, initialPosition ) {

// @public {number} - the side length of the squares that make up the ten frame
this.squareSideLength = squareSideLength;

// @public {Vector2[]}
this.spotCenters = TenFrameNode.getSpotCenters( {
sideLength: squareSideLength
} );

// @public {Vector2Property}
this.positionProperty = new Vector2Property( initialPosition );
}
}

return numberPlay.register( 'TenFrame', TenFrame );
} );
60 changes: 60 additions & 0 deletions js/lab/view/DraggableTenFrameNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2019, University of Colorado Boulder

/**
* A ten frame node that can be dragged around and hold play objects.
*
* @author Chris Klusendorf (PhET Interactive Simulations)
*/
define( require => {
'use strict';

// modules
const DragListener = require( 'SCENERY/listeners/DragListener' );
const Node = require( 'SCENERY/nodes/Node' );
const numberPlay = require( 'NUMBER_PLAY/numberPlay' );
const TenFrameNode = require( 'NUMBER_PLAY/common/view/TenFrameNode' );
const Vector2 = require( 'DOT/Vector2' );

class DraggableTenFrameNode extends Node {

/**
* @param {number} sideLength - see doc below
* @param {Vector2} initialPosition
*/
constructor( tenFrame, modelViewTransform, dropListener ) {
super();

// @public {TenFrame}
this.tenFrame = tenFrame;

const tenFrameNode = TenFrameNode.getTenFramePath( {
sideLength: tenFrame.squareSideLength
} );
tenFrameNode.center = new Vector2( 0, -tenFrameNode.height / 2 );
this.addChild( tenFrameNode );

// @public {DragListener}
this.dragListener = new DragListener( {
targetNode: this,
transform: modelViewTransform,
locationProperty: tenFrame.positionProperty,
end: () => {
dropListener();
}
} );

this.cursor = 'pointer';
// TODO: add a 'dragging' bar and make only that have a forwarding listener
this.inputListeners = [ DragListener.createForwardingListener( event => {
this.dragListener.press( event, this );
this.moveToFront();
} ) ];

tenFrame.positionProperty.link( position => {
this.translation = modelViewTransform.modelToViewPosition( position );
} );
}
}

return numberPlay.register( 'DraggableTenFrameNode', DraggableTenFrameNode );
} );
Loading

0 comments on commit 2a85a60

Please sign in to comment.