From 2106d7e5ca54a2545e4b179840c2e404a339a16f Mon Sep 17 00:00:00 2001 From: Daniel Mane Date: Tue, 29 Apr 2014 12:43:11 -0700 Subject: [PATCH 1/6] Split AreaInteraction into dragInteraction, dragBoxInteraction, xDragBoxInteraction, xyDragBoxInteraction --- src/interactions/areaInteraction.ts | 100 ------------------ src/interactions/drag/dragBoxInteraction.ts | 31 ++++++ src/interactions/drag/dragInteraction.ts | 77 ++++++++++++++ src/interactions/drag/xDragBoxInteraction.ts | 24 +++++ src/interactions/drag/xyDragBoxInteraction.ts | 27 +++++ src/reference.ts | 5 +- test/interactionTests.ts | 19 ++-- 7 files changed, 173 insertions(+), 110 deletions(-) delete mode 100644 src/interactions/areaInteraction.ts create mode 100644 src/interactions/drag/dragBoxInteraction.ts create mode 100644 src/interactions/drag/dragInteraction.ts create mode 100644 src/interactions/drag/xDragBoxInteraction.ts create mode 100644 src/interactions/drag/xyDragBoxInteraction.ts diff --git a/src/interactions/areaInteraction.ts b/src/interactions/areaInteraction.ts deleted file mode 100644 index 97440fa994..0000000000 --- a/src/interactions/areaInteraction.ts +++ /dev/null @@ -1,100 +0,0 @@ -/// - -module Plottable { - export class AreaInteraction extends Interaction { - private static CLASS_DRAG_BOX = "drag-box"; - private dragInitialized = false; - private dragBehavior: D3.Behavior.Drag; - private origin = [0,0]; - private location = [0,0]; - private constrainX: (n: number) => number; - private constrainY: (n: number) => number; - private dragBox: D3.Selection; - private callbackToCall: (area: SelectionArea) => any; - - /** - * Creates an AreaInteraction. - * - * @param {Component} componentToListenTo The component to listen for interactions on. - */ - constructor(componentToListenTo: Component) { - super(componentToListenTo); - this.dragBehavior = d3.behavior.drag(); - this.dragBehavior.on("dragstart", () => this.dragstart()); - this.dragBehavior.on("drag", () => this.drag ()); - this.dragBehavior.on("dragend", () => this.dragend ()); - } - - /** - * Adds a callback to be called when the AreaInteraction triggers. - * - * @param {(a: SelectionArea) => any} cb The function to be called. Takes in a SelectionArea in pixels. - * @returns {AreaInteraction} The calling AreaInteraction. - */ - public callback(cb?: (a: SelectionArea) => any): AreaInteraction { - this.callbackToCall = cb; - return this; - } - - private dragstart(){ - this.clearBox(); - var availableWidth = parseFloat(this.hitBox.attr("width")); - var availableHeight = parseFloat(this.hitBox.attr("height")); - // the constraint functions ensure that the selection rectangle will not exceed the hit box - var constraintFunction = (min: number, max: number) => (x: number) => Math.min(Math.max(x, min), max); - this.constrainX = constraintFunction(0, availableWidth); - this.constrainY = constraintFunction(0, availableHeight); - } - - private drag(){ - if (!this.dragInitialized) { - this.origin = [d3.event.x, d3.event.y]; - this.dragInitialized = true; - } - - this.location = [this.constrainX(d3.event.x), this.constrainY(d3.event.y)]; - var width = Math.abs(this.origin[0] - this.location[0]); - var height = Math.abs(this.origin[1] - this.location[1]); - var x = Math.min(this.origin[0], this.location[0]); - var y = Math.min(this.origin[1], this.location[1]); - this.dragBox.attr("x", x).attr("y", y).attr("height", height).attr("width", width); - } - - private dragend(){ - if (!this.dragInitialized) { - return; - } - - this.dragInitialized = false; - if (this.callbackToCall == null) { - return; - } - - var xMin = Math.min(this.origin[0], this.location[0]); - var xMax = Math.max(this.origin[0], this.location[0]); - var yMin = Math.min(this.origin[1], this.location[1]); - var yMax = Math.max(this.origin[1], this.location[1]); - var pixelArea = {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax}; - this.callbackToCall(pixelArea); - } - - /** - * Clears the highlighted drag-selection box drawn by the AreaInteraction. - * - * @returns {AreaInteraction} The calling AreaInteraction. - */ - public clearBox(): AreaInteraction { - this.dragBox.attr("height", 0).attr("width", 0); - return this; - } - - public _anchor(hitBox: D3.Selection): AreaInteraction { - super._anchor(hitBox); - var cname = AreaInteraction.CLASS_DRAG_BOX; - var background = this.componentToListenTo.backgroundContainer; - this.dragBox = background.append("rect").classed(cname, true).attr("x", 0).attr("y", 0); - hitBox.call(this.dragBehavior); - return this; - } - } -} diff --git a/src/interactions/drag/dragBoxInteraction.ts b/src/interactions/drag/dragBoxInteraction.ts new file mode 100644 index 0000000000..3b4430a858 --- /dev/null +++ b/src/interactions/drag/dragBoxInteraction.ts @@ -0,0 +1,31 @@ +/// + +module Plottable { + export class DragBoxInteraction extends DragInteraction { + private static CLASS_DRAG_BOX = "drag-box"; + public dragBox: D3.Selection; + + public _dragstart() { + super._dragstart(); + this.clearBox(); + } + + /** + * Clears the highlighted drag-selection box drawn by the AreaInteraction. + * + * @returns {AreaInteraction} The calling AreaInteraction. + */ + public clearBox() { + this.dragBox.attr("height", 0).attr("width", 0); + return this; + } + + public _anchor(hitBox: D3.Selection) { + super._anchor(hitBox); + var cname = DragBoxInteraction.CLASS_DRAG_BOX; + var background = this.componentToListenTo.backgroundContainer; + this.dragBox = background.append("rect").classed(cname, true).attr("x", 0).attr("y", 0); + return this; + } + } +} diff --git a/src/interactions/drag/dragInteraction.ts b/src/interactions/drag/dragInteraction.ts new file mode 100644 index 0000000000..073d9bda95 --- /dev/null +++ b/src/interactions/drag/dragInteraction.ts @@ -0,0 +1,77 @@ +/// + +module Plottable { + export class DragInteraction extends Interaction { + private dragInitialized = false; + private dragBehavior: D3.Behavior.Drag; + public origin = [0,0]; + public location = [0,0]; + private constrainX: (n: number) => number; + private constrainY: (n: number) => number; + public callbackToCall: (dragInfo: any) => any; + + /** + * Creates a DragInteraction. + * + * @param {Component} componentToListenTo The component to listen for interactions on. + */ + constructor(componentToListenTo: Component) { + super(componentToListenTo); + this.dragBehavior = d3.behavior.drag(); + this.dragBehavior.on("dragstart", () => this._dragstart()); + this.dragBehavior.on("drag", () => this._drag ()); + this.dragBehavior.on("dragend", () => this._dragend ()); + } + + /** + * Adds a callback to be called when the AreaInteraction triggers. + * + * @param {(a: SelectionArea) => any} cb The function to be called. Takes in a SelectionArea in pixels. + * @returns {AreaInteraction} The calling AreaInteraction. + */ + public callback(cb?: (a: any) => any) { + this.callbackToCall = cb; + return this; + } + + public _dragstart(){ + var availableWidth = parseFloat(this.hitBox.attr("width")); + var availableHeight = parseFloat(this.hitBox.attr("height")); + // the constraint functions ensure that the selection rectangle will not exceed the hit box + var constraintFunction = (min: number, max: number) => (x: number) => Math.min(Math.max(x, min), max); + this.constrainX = constraintFunction(0, availableWidth); + this.constrainY = constraintFunction(0, availableHeight); + } + + public _drag(){ + if (!this.dragInitialized) { + this.origin = [d3.event.x, d3.event.y]; + this.dragInitialized = true; + } + + this.location = [this.constrainX(d3.event.x), this.constrainY(d3.event.y)]; + } + + public _dragend(){ + if (!this.dragInitialized) { + return; + } + this.dragInitialized = false; + this._doDragend(); + } + + public _doDragend() { + // seperated out so it can be over-ridden by dragInteractions that want to pass out diff information + // eg just x values for an xSelectionInteraction + if (this.callbackToCall != null) { + this.callbackToCall([this.origin, this.location]); + } + } + + public _anchor(hitBox: D3.Selection) { + super._anchor(hitBox); + hitBox.call(this.dragBehavior); + return this; + } + } +} diff --git a/src/interactions/drag/xDragBoxInteraction.ts b/src/interactions/drag/xDragBoxInteraction.ts new file mode 100644 index 0000000000..7e49688dde --- /dev/null +++ b/src/interactions/drag/xDragBoxInteraction.ts @@ -0,0 +1,24 @@ +/// + +module Plottable { + export class XAreaInteraction extends DragBoxInteraction { + public _drag(){ + super._drag(); + var width = Math.abs(this.origin[0] - this.location[0]); + var height = parseFloat(this.hitBox.attr("height")); + var x = Math.min(this.origin[0] , this.location[0]); + var y = 0 + this.dragBox.attr({x: x, y: y, height: height, width: width}); + } + + public _doDragend(){ + if (this.callbackToCall == null) { + return; + } + var xMin = Math.min(this.origin[0], this.location[0]); + var xMax = Math.max(this.origin[0], this.location[0]); + var pixelArea = {xMin: xMin, xMax: xMax}; + this.callbackToCall(pixelArea); + } + } +} diff --git a/src/interactions/drag/xyDragBoxInteraction.ts b/src/interactions/drag/xyDragBoxInteraction.ts new file mode 100644 index 0000000000..f39b1d9381 --- /dev/null +++ b/src/interactions/drag/xyDragBoxInteraction.ts @@ -0,0 +1,27 @@ +/// + +module Plottable { + export class XYDragBoxInteraction extends DragBoxInteraction { + public _drag(){ + super._drag(); + var width = Math.abs(this.origin[0] - this.location[0]); + var height = Math.abs(this.origin[1] - this.location[1]); + var x = Math.min(this.origin[0] , this.location[0]); + var y = Math.min(this.origin[1] , this.location[1]); + this.dragBox.attr({x: x, y: y, height: height, width: width}); + } + + public _doDragend(){ + if (this.callbackToCall == null) { + return; + } + + var xMin = Math.min(this.origin[0], this.location[0]); + var xMax = Math.max(this.origin[0], this.location[0]); + var yMin = Math.min(this.origin[1], this.location[1]); + var yMax = Math.max(this.origin[1], this.location[1]); + var pixelArea = {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax}; + this.callbackToCall(pixelArea); + } + } +} diff --git a/src/reference.ts b/src/reference.ts index 49675cebee..f51b78f2d5 100644 --- a/src/reference.ts +++ b/src/reference.ts @@ -33,10 +33,13 @@ /// /// -/// /// /// /// /// +/// +/// +/// +/// /// diff --git a/test/interactionTests.ts b/test/interactionTests.ts index e17b55c103..a6d623a62a 100644 --- a/test/interactionTests.ts +++ b/test/interactionTests.ts @@ -20,12 +20,12 @@ function makeFakeEvent(x: number, y: number): D3.Event { } function fakeDragSequence(anyedInteraction: any, startX: number, startY: number, endX: number, endY: number) { - anyedInteraction.dragstart(); + anyedInteraction._dragstart(); d3.event = makeFakeEvent(startX, startY); - anyedInteraction.drag(); + anyedInteraction._drag(); d3.event = makeFakeEvent(endX, endY); - anyedInteraction.drag(); - anyedInteraction.dragend(); + anyedInteraction._drag(); + anyedInteraction._dragend(); d3.event = null; } @@ -78,7 +78,7 @@ describe("Interactions", () => { }); }); - describe("AreaInteraction", () => { + describe("XYDragBoxInteraction", () => { var svgWidth = 400; var svgHeight = 400; var svg: D3.Selection; @@ -86,7 +86,7 @@ describe("Interactions", () => { var xScale: Plottable.QuantitiveScale; var yScale: Plottable.QuantitiveScale; var renderer: Plottable.XYRenderer; - var interaction: Plottable.AreaInteraction; + var interaction: Plottable.XYDragBoxInteraction; var dragstartX = 20; var dragstartY = svgHeight-100; @@ -100,12 +100,13 @@ describe("Interactions", () => { yScale = new Plottable.LinearScale(); renderer = new Plottable.CircleRenderer(dataset, xScale, yScale); renderer.renderTo(svg); - interaction = new Plottable.AreaInteraction(renderer); + interaction = new Plottable.XYDragBoxInteraction(renderer); interaction.registerWithComponent(); }); afterEach(() => { - interaction.callback().clearBox(); + interaction.callback(); + interaction.clearBox(); }); it("All callbacks are notified with appropriate data when a drag finishes", () => { @@ -132,7 +133,7 @@ describe("Interactions", () => { it("Highlights and un-highlights areas appropriately", () => { fakeDragSequence(( interaction), dragstartX, dragstartY, dragendX, dragendY); - var dragBoxClass = "." + ( Plottable.AreaInteraction).CLASS_DRAG_BOX; + var dragBoxClass = "." + ( Plottable.XYDragBoxInteraction).CLASS_DRAG_BOX; var dragBox = renderer.backgroundContainer.select(dragBoxClass); assert.isNotNull(dragBox, "the dragbox was created"); var actualStartPosition = {x: parseFloat(dragBox.attr("x")), y: parseFloat(dragBox.attr("y"))}; From fc0339b2c2dba5566da27346d1f15dc8b34ce0bc Mon Sep 17 00:00:00 2001 From: Daniel Mane Date: Tue, 29 Apr 2014 12:51:26 -0700 Subject: [PATCH 2/6] Add a selection quicktest, fix a mistake in XDragBoxInteraction --- quicktests/quicktest-selection.html | 77 ++++++++++++++++++++ src/interactions/drag/xDragBoxInteraction.ts | 2 +- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 quicktests/quicktest-selection.html diff --git a/quicktests/quicktest-selection.html b/quicktests/quicktest-selection.html new file mode 100644 index 0000000000..6f299a0be8 --- /dev/null +++ b/quicktests/quicktest-selection.html @@ -0,0 +1,77 @@ + + + Selection Quicktest + + + + + + + + + + + + + diff --git a/src/interactions/drag/xDragBoxInteraction.ts b/src/interactions/drag/xDragBoxInteraction.ts index 7e49688dde..540ecf1dd0 100644 --- a/src/interactions/drag/xDragBoxInteraction.ts +++ b/src/interactions/drag/xDragBoxInteraction.ts @@ -1,7 +1,7 @@ /// module Plottable { - export class XAreaInteraction extends DragBoxInteraction { + export class XDragBoxInteraction extends DragBoxInteraction { public _drag(){ super._drag(); var width = Math.abs(this.origin[0] - this.location[0]); From d8752323e4d432a016602091c39407acd63dd7af Mon Sep 17 00:00:00 2001 From: Daniel Mane Date: Tue, 29 Apr 2014 12:53:23 -0700 Subject: [PATCH 3/6] Fix typo in reference.ts --- src/reference.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference.ts b/src/reference.ts index f51b78f2d5..c0b9c624a6 100644 --- a/src/reference.ts +++ b/src/reference.ts @@ -40,6 +40,6 @@ /// /// /// -/// +/// /// From e753a1303bebc68fde238b0f1de048beb5860c41 Mon Sep 17 00:00:00 2001 From: Daniel Mane Date: Tue, 29 Apr 2014 13:10:50 -0700 Subject: [PATCH 4/6] Get width/height from component, not parseFloat --- src/interactions/drag/dragInteraction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interactions/drag/dragInteraction.ts b/src/interactions/drag/dragInteraction.ts index 073d9bda95..3f56e9e1f9 100644 --- a/src/interactions/drag/dragInteraction.ts +++ b/src/interactions/drag/dragInteraction.ts @@ -35,8 +35,8 @@ module Plottable { } public _dragstart(){ - var availableWidth = parseFloat(this.hitBox.attr("width")); - var availableHeight = parseFloat(this.hitBox.attr("height")); + var availableWidth = this.componentToListenTo.availableWidth; + var availableHeight = this.componentToListenTo.availableHeight; // the constraint functions ensure that the selection rectangle will not exceed the hit box var constraintFunction = (min: number, max: number) => (x: number) => Math.min(Math.max(x, min), max); this.constrainX = constraintFunction(0, availableWidth); From 9d540f360e22c0693c4227f24ab4776e40c89fe9 Mon Sep 17 00:00:00 2001 From: Daniel Mane Date: Tue, 29 Apr 2014 13:11:14 -0700 Subject: [PATCH 5/6] Clearer console logging, better fn names --- quicktests/quicktest-selection.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/quicktests/quicktest-selection.html b/quicktests/quicktest-selection.html index 6f299a0be8..a32845393c 100644 --- a/quicktests/quicktest-selection.html +++ b/quicktests/quicktest-selection.html @@ -9,11 +9,11 @@