Skip to content

Commit

Permalink
Add dropping logic to keep addend visible buttons clear, see: #38
Browse files Browse the repository at this point in the history
  • Loading branch information
marlitas committed Dec 6, 2024
1 parent 444f531 commit 826cdf2
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
2 changes: 1 addition & 1 deletion js/common/NumberPairsConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const NumberPairsConstants = {
COUNTING_AREA_CORNER_RADIUS: 5,
COUNTING_AREA_BOUNDS: new Bounds2( ScreenView.DEFAULT_LAYOUT_BOUNDS.minX + COUNTING_AREA_X_MARGIN,
COUNTING_AREA_MIN_Y, ScreenView.DEFAULT_LAYOUT_BOUNDS.maxX - COUNTING_AREA_X_MARGIN, COUNTING_AREA_MIN_Y + 340 ),

COUNTING_AREA_INNER_MARGIN: 5,
TEN_TOTAL_RANGE: TEN_TOTAL_RANGE,
TEN_NUMBER_LINE_RANGE: new Range( 0, TEN_TOTAL_RANGE.max ),
TWENTY_TOTAL_RANGE: TWENTY_TOTAL_RANGE,
Expand Down
67 changes: 64 additions & 3 deletions js/common/model/NumberPairsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import numberPairs from '../../numberPairs.js';
import NumberPairsConstants from '../NumberPairsConstants.js';
import CountingObject, { AddendType, KITTEN_PANEL_WIDTH } from './CountingObject.js';
import RepresentationType from './RepresentationType.js';
import Dimension2 from '../../../../dot/js/Dimension2.js';

type AnimationTarget = {
property: Property<Vector2>;
Expand Down Expand Up @@ -228,15 +229,75 @@ export default class NumberPairsModel implements TModel {
} );
}

private getValidDropPoint( invalidBounds: Bounds2, proposedPoint: Vector2, allowedDirection: 'upLeft' | 'upRight' ): Vector2 {

if ( invalidBounds.containsPoint( proposedPoint ) ) {
const closestXEdge = allowedDirection === 'upLeft' ? invalidBounds.minX : invalidBounds.maxX;
const closestYEdge = invalidBounds.minY;

if ( Math.abs( closestXEdge - proposedPoint.x ) < Math.abs( closestYEdge - proposedPoint.y ) ) {
return new Vector2( closestXEdge, proposedPoint.y );
}
else {
return new Vector2( proposedPoint.x, closestYEdge );
}
}
else {
return proposedPoint;
}
}

private sendToValidDropPoint( countingObject: CountingObject, positionPropertyType: 'attribute' | 'location' ): Vector2 {
const countingAreaBounds = NumberPairsConstants.COUNTING_AREA_BOUNDS;
const positionProperty = positionPropertyType === 'attribute' ? countingObject.attributePositionProperty :
countingObject.locationPositionProperty;

const addendVisibleButtonDimension = new Dimension2(
NumberPairsConstants.RECTANGULAR_PUSH_BUTTON_OPTIONS.size.width + NumberPairsConstants.COUNTING_AREA_INNER_MARGIN + DROP_ZONE_MARGIN,
NumberPairsConstants.RECTANGULAR_PUSH_BUTTON_OPTIONS.size.height + NumberPairsConstants.COUNTING_AREA_INNER_MARGIN + DROP_ZONE_MARGIN
);
const leftAddendVisibleButtonBounds = new Bounds2(
countingAreaBounds.minX,
countingAreaBounds.maxY - addendVisibleButtonDimension.height,
countingAreaBounds.minX + addendVisibleButtonDimension.width,
countingAreaBounds.maxY );
const invalidDropBounds = positionPropertyType === 'attribute' ? [ leftAddendVisibleButtonBounds ] :
[
leftAddendVisibleButtonBounds,
leftAddendVisibleButtonBounds.shiftedX( countingAreaBounds.width - addendVisibleButtonDimension.width )
];

let validDropPoint = positionProperty.value;
invalidDropBounds.forEach( ( bounds, i ) => {

// The first bounds is on the left side of the counting area
const direction = i === 0 ? 'upRight' : 'upLeft';
validDropPoint = this.getValidDropPoint( bounds, validDropPoint, direction );
} );

const animation = new Animation( {
duration: 0.4,
targets: [ {
property: positionProperty,
to: validDropPoint
} ]
} );
animation.start();
return validDropPoint;
}


/**
* Animates the dropped counting object and any overlapping objects to the closest boundary point of the drop zone.
* @param droppedCountingObject
* @param positionPropertyType
*
* // TODO: We still need to handle when a point is calculated to be outside of the Counting Area bounds.
*/
public dropCountingObject( droppedCountingObject: CountingObject, positionPropertyType: 'attribute' | 'location' ): void {
const dropZoneBounds = positionPropertyType === 'attribute' ?
this.getDropZoneBounds( droppedCountingObject.attributePositionProperty.value ) :
this.getDropZoneBounds( droppedCountingObject.locationPositionProperty.value );

const countingObjectValidDropPoint = this.sendToValidDropPoint( droppedCountingObject, positionPropertyType );
const dropZoneBounds = this.getDropZoneBounds( countingObjectValidDropPoint );
const activeCountingObjects = this.countingObjects.filter( countingObject =>
countingObject.addendTypeProperty.value !== AddendType.INACTIVE && countingObject !== droppedCountingObject );

Expand Down
2 changes: 1 addition & 1 deletion js/common/view/CountingAreaNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type SelfOptions = {
type CountingAreaNodeOptions = SelfOptions & StrictOmit<NodeOptions, 'children'> & PickRequired<NodeOptions, 'tandem'>;

export const COUNTING_AREA_LINE_WIDTH = 1.5;
export const COUNTING_AREA_MARGIN = 5;
export const COUNTING_AREA_MARGIN = NumberPairsConstants.COUNTING_AREA_INNER_MARGIN;
export default class CountingAreaNode extends Node {

public constructor(
Expand Down

0 comments on commit 826cdf2

Please sign in to comment.