mirror of
https://github.com/google/blockly.git
synced 2026-03-11 15:50:10 +01:00
Merge pull request #266 from rachel-fenichel/feature/connection_db_testing
Connection db testing
This commit is contained in:
@@ -29,6 +29,7 @@ goog.provide('Blockly.BlockSvg.render');
|
||||
|
||||
goog.require('Blockly.BlockSvg');
|
||||
|
||||
|
||||
// UI constants for rendering blocks.
|
||||
/**
|
||||
* Horizontal space between elements.
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Connection');
|
||||
goog.provide('Blockly.ConnectionDB');
|
||||
|
||||
goog.require('goog.dom');
|
||||
|
||||
@@ -153,6 +152,18 @@ Blockly.Connection.prototype.isSuperior = function() {
|
||||
this.type == Blockly.NEXT_STATEMENT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance between this connection and another connection.
|
||||
* @param {!Blockly.Connection} otherConnection The other connection to measure
|
||||
* the distance to.
|
||||
* @return {number} The distance between connections.
|
||||
*/
|
||||
Blockly.Connection.prototype.distanceFrom = function(otherConnection) {
|
||||
var xDiff = this.x_ - otherConnection.x_;
|
||||
var yDiff = this.y_ - otherConnection.y_;
|
||||
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the current connection can connect with the target
|
||||
* connection.
|
||||
@@ -207,6 +218,60 @@ Blockly.Connection.prototype.checkConnection_ = function(target) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the two connections can be dragged to connect to each other.
|
||||
* @param {Blockly.Connection} candidate A nearby connection to check.
|
||||
* @param {number} maxRadius The maximum radius allowed for connections.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
*/
|
||||
Blockly.Connection.prototype.isConnectionAllowed = function(candidate,
|
||||
maxRadius) {
|
||||
if (this.distanceFrom(candidate) > maxRadius) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Type checking
|
||||
var canConnect = this.canConnectWithReason_(candidate);
|
||||
if (canConnect != Blockly.Connection.CAN_CONNECT &&
|
||||
canConnect != Blockly.Connection.REASON_MUST_DISCONNECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug. Don't offer to connect the
|
||||
// bottom of a statement block to one that's already connected.
|
||||
if (candidate.type == Blockly.OUTPUT_VALUE ||
|
||||
candidate.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
if (candidate.targetConnection || this.targetConnection) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an unmovable block.
|
||||
if (candidate.type == Blockly.INPUT_VALUE &&
|
||||
candidate.targetConnection &&
|
||||
!candidate.targetBlock().isMovable() &&
|
||||
!candidate.targetBlock().isShadow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
var targetSourceBlock = candidate.sourceBlock_;
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
if (targetSourceBlock && sourceBlock) {
|
||||
do {
|
||||
if (sourceBlock == targetSourceBlock) {
|
||||
return false;
|
||||
}
|
||||
targetSourceBlock = targetSourceBlock.getParent();
|
||||
} while (targetSourceBlock);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect this connection to another connection.
|
||||
* @param {!Blockly.Connection} otherConnection Connection to connect to.
|
||||
@@ -332,7 +397,7 @@ Blockly.Connection.connectReciprocally = function(first, second) {
|
||||
}
|
||||
first.targetConnection = second;
|
||||
second.targetConnection = first;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Does the given block have one and only one connection point that will accept
|
||||
@@ -503,7 +568,7 @@ Blockly.Connection.prototype.moveTo = function(x, y) {
|
||||
this.y_ = y;
|
||||
// Insert it into its new location in the database.
|
||||
if (!this.hidden_) {
|
||||
this.db_.addConnection_(this);
|
||||
this.db_.addConnection(this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -516,38 +581,6 @@ Blockly.Connection.prototype.moveBy = function(dx, dy) {
|
||||
this.moveTo(this.x_ + dx, this.y_ + dy);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add highlighting around this connection.
|
||||
*/
|
||||
Blockly.Connection.prototype.highlight = function() {
|
||||
var steps;
|
||||
if (this.type == Blockly.INPUT_VALUE || this.type == Blockly.OUTPUT_VALUE) {
|
||||
var tabWidth = this.sourceBlock_.RTL ? -Blockly.BlockSvg.TAB_WIDTH :
|
||||
Blockly.BlockSvg.TAB_WIDTH;
|
||||
steps = 'm 0,0 ' + Blockly.BlockSvg.TAB_PATH_DOWN + ' v 5';
|
||||
|
||||
} else {
|
||||
steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5';
|
||||
}
|
||||
var xy = this.sourceBlock_.getRelativeToSurfaceXY();
|
||||
var x = this.x_ - xy.x;
|
||||
var y = this.y_ - xy.y;
|
||||
Blockly.Connection.highlightedPath_ = Blockly.createSvgElement('path',
|
||||
{'class': 'blocklyHighlightedConnectionPath',
|
||||
'd': steps,
|
||||
transform: 'translate(' + x + ',' + y + ')' +
|
||||
(this.sourceBlock_.RTL ? ' scale(-1 1)' : '')},
|
||||
this.sourceBlock_.getSvgRoot());
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the highlighting around this connection.
|
||||
*/
|
||||
Blockly.Connection.prototype.unhighlight = function() {
|
||||
goog.dom.removeNode(Blockly.Connection.highlightedPath_);
|
||||
delete Blockly.Connection.highlightedPath_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the blocks on either side of this connection right next to each other.
|
||||
* @private
|
||||
@@ -575,108 +608,18 @@ Blockly.Connection.prototype.tighten_ = function() {
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @param {number} dy Vertical offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two properties: 'connection' which is either
|
||||
* another connection or null, and 'radius' which is the distance.
|
||||
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two
|
||||
* properties:' connection' which is either another connection or null,
|
||||
* and 'radius' which is the distance.
|
||||
*/
|
||||
Blockly.Connection.prototype.closest = function(maxLimit, dx, dy) {
|
||||
if (this.targetConnection) {
|
||||
// Don't offer to connect to a connection that's already connected.
|
||||
return {connection: null, radius: maxLimit};
|
||||
var closestConnection = this.dbOpposite_.searchForClosest(this, maxLimit, dx,
|
||||
dy);
|
||||
if (closestConnection) {
|
||||
return {connection: closestConnection,
|
||||
radius: this.distanceFrom(closestConnection)};
|
||||
}
|
||||
// Determine the opposite type of connection.
|
||||
var db = this.dbOpposite_;
|
||||
|
||||
// Since this connection is probably being dragged, add the delta.
|
||||
var currentX = this.x_ + dx;
|
||||
var currentY = this.y_ + dy;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var closestConnection = null;
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
var thisConnection = this;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the search needs to continue: either the current
|
||||
* connection's vertical distance from the other connection is less than
|
||||
* the allowed radius, or if the connection is not compatible.
|
||||
* @private
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var connection = db[yIndex];
|
||||
if (connection.type == Blockly.OUTPUT_VALUE ||
|
||||
connection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug. Don't offer to connect the
|
||||
// bottom of a statement block to one that's already connected.
|
||||
if (connection.targetConnection) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Offering to connect the top of a statement block to an already connected
|
||||
// connection is ok, we'll just insert it into the stack.
|
||||
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an unmovable block.
|
||||
if (connection.type == Blockly.INPUT_VALUE &&
|
||||
connection.targetConnection &&
|
||||
!connection.targetBlock().isMovable() &&
|
||||
!connection.targetBlock().isShadow()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do type checking.
|
||||
if (!thisConnection.checkType_(connection)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
var targetSourceBlock = connection.sourceBlock_;
|
||||
do {
|
||||
if (sourceBlock == targetSourceBlock) {
|
||||
return true;
|
||||
}
|
||||
targetSourceBlock = targetSourceBlock.getParent();
|
||||
} while (targetSourceBlock);
|
||||
|
||||
// Only connections within the maxLimit radius.
|
||||
var dx = currentX - connection.x_;
|
||||
var dy = currentY - connection.y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxLimit) {
|
||||
closestConnection = connection;
|
||||
maxLimit = r;
|
||||
}
|
||||
return Math.abs(dy) < maxLimit;
|
||||
}
|
||||
return {connection: closestConnection, radius: maxLimit};
|
||||
return {connection: null, radius: maxLimit};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -763,59 +706,11 @@ Blockly.Connection.prototype.getShadowDom = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Connection.prototype.neighbours_ = function(maxLimit) {
|
||||
// Determine the opposite type of connection.
|
||||
var db = this.dbOpposite_;
|
||||
|
||||
var currentX = this.x_;
|
||||
var currentY = this.y_;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var neighbours = [];
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the current connection's vertical distance from
|
||||
* the other connection is less than the allowed radius.
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var dx = currentX - db[yIndex].x_;
|
||||
var dy = currentY - db[yIndex].y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxLimit) {
|
||||
neighbours.push(db[yIndex]);
|
||||
}
|
||||
return dy < maxLimit;
|
||||
}
|
||||
return neighbours;
|
||||
return this.dbOpposite_.getNeighbours(this, maxLimit);
|
||||
};
|
||||
|
||||
// Appearance or lack thereof.
|
||||
|
||||
/**
|
||||
* Set whether this connections is hidden (not tracked in a database) or not.
|
||||
* @param {boolean} hidden True if connection is hidden.
|
||||
@@ -825,7 +720,7 @@ Blockly.Connection.prototype.setHidden = function(hidden) {
|
||||
if (hidden && this.inDB_) {
|
||||
this.db_.removeConnection_(this);
|
||||
} else if (!hidden && !this.inDB_) {
|
||||
this.db_.addConnection_(this);
|
||||
this.db_.addConnection(this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -895,110 +790,34 @@ Blockly.Connection.prototype.unhideAll = function() {
|
||||
return renderList;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Database of connections.
|
||||
* Connections are stored in order of their vertical component. This way
|
||||
* connections in an area may be looked up quickly using a binary search.
|
||||
* @constructor
|
||||
* Add highlighting around this connection.
|
||||
*/
|
||||
Blockly.ConnectionDB = function() {
|
||||
};
|
||||
Blockly.Connection.prototype.highlight = function() {
|
||||
var steps;
|
||||
if (this.type == Blockly.INPUT_VALUE || this.type == Blockly.OUTPUT_VALUE) {
|
||||
var tabWidth = this.sourceBlock_.RTL ? -Blockly.BlockSvg.TAB_WIDTH :
|
||||
Blockly.BlockSvg.TAB_WIDTH;
|
||||
steps = 'm 0,0 ' + Blockly.BlockSvg.TAB_PATH_DOWN + ' v 5';
|
||||
|
||||
Blockly.ConnectionDB.prototype = new Array();
|
||||
/**
|
||||
* Don't inherit the constructor from Array.
|
||||
* @type {!Function}
|
||||
*/
|
||||
Blockly.ConnectionDB.constructor = Blockly.ConnectionDB;
|
||||
|
||||
/**
|
||||
* Add a connection to the database. Must not already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be added.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.addConnection_ = function(connection) {
|
||||
if (connection.inDB_) {
|
||||
throw 'Connection already in database.';
|
||||
} else {
|
||||
steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5';
|
||||
}
|
||||
if (connection.sourceBlock_.isInFlyout) {
|
||||
// Don't bother maintaining a database of connections in a flyout.
|
||||
return;
|
||||
}
|
||||
// Insert connection using binary search.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length;
|
||||
while (pointerMin < pointerMax) {
|
||||
var pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid + 1;
|
||||
} else if (this[pointerMid].y_ > connection.y_) {
|
||||
pointerMax = pointerMid;
|
||||
} else {
|
||||
pointerMin = pointerMid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.splice(pointerMin, 0, connection);
|
||||
connection.inDB_ = true;
|
||||
var xy = this.sourceBlock_.getRelativeToSurfaceXY();
|
||||
var x = this.x_ - xy.x;
|
||||
var y = this.y_ - xy.y;
|
||||
Blockly.Connection.highlightedPath_ = Blockly.createSvgElement('path',
|
||||
{'class': 'blocklyHighlightedConnectionPath',
|
||||
'd': steps,
|
||||
transform: 'translate(' + x + ',' + y + ')' +
|
||||
(this.sourceBlock_.RTL ? ' scale(-1 1)' : '')},
|
||||
this.sourceBlock_.getSvgRoot());
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a connection from the database. Must already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be removed.
|
||||
* @private
|
||||
* Remove the highlighting around this connection.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) {
|
||||
if (!connection.inDB_) {
|
||||
throw 'Connection not in database.';
|
||||
}
|
||||
connection.inDB_ = false;
|
||||
// Find the connection using a binary search.
|
||||
// About 10% faster than a linear search using indexOf.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the connection.
|
||||
// When found, splice it out of the array.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
while (pointerMin >= 0 && this[pointerMin].y_ == connection.y_) {
|
||||
if (this[pointerMin] == connection) {
|
||||
this.splice(pointerMin, 1);
|
||||
return;
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
if (this[pointerMax] == connection) {
|
||||
this.splice(pointerMax, 1);
|
||||
return;
|
||||
}
|
||||
pointerMax++;
|
||||
} while (pointerMax < this.length &&
|
||||
this[pointerMax].y_ == connection.y_);
|
||||
throw 'Unable to find connection in connectionDB.';
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a set of connection DBs for a specified workspace.
|
||||
* @param {!Blockly.Workspace} workspace The workspace this DB is for.
|
||||
*/
|
||||
Blockly.ConnectionDB.init = function(workspace) {
|
||||
// Create four databases, one for each connection type.
|
||||
var dbList = [];
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB();
|
||||
workspace.connectionDBList = dbList;
|
||||
Blockly.Connection.prototype.unhighlight = function() {
|
||||
goog.dom.removeNode(Blockly.Connection.highlightedPath_);
|
||||
delete Blockly.Connection.highlightedPath_;
|
||||
};
|
||||
|
||||
291
core/connection_db.js
Normal file
291
core/connection_db.js
Normal file
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2011 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Components for managing connections between blocks.
|
||||
* @author fraser@google.com (Neil Fraser)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ConnectionDB');
|
||||
|
||||
goog.require('Blockly.Connection');
|
||||
|
||||
|
||||
/**
|
||||
* Database of connections.
|
||||
* Connections are stored in order of their vertical component. This way
|
||||
* connections in an area may be looked up quickly using a binary search.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ConnectionDB = function() {
|
||||
};
|
||||
|
||||
Blockly.ConnectionDB.prototype = new Array();
|
||||
/**
|
||||
* Don't inherit the constructor from Array.
|
||||
* @type {!Function}
|
||||
*/
|
||||
Blockly.ConnectionDB.constructor = Blockly.ConnectionDB;
|
||||
|
||||
/**
|
||||
* Add a connection to the database. Must not already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be added.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.addConnection = function(connection) {
|
||||
if (connection.inDB_) {
|
||||
throw 'Connection already in database.';
|
||||
}
|
||||
if (connection.sourceBlock_.isInFlyout) {
|
||||
// Don't bother maintaining a database of connections in a flyout.
|
||||
return;
|
||||
}
|
||||
var position = this.findPositionForConnection_(connection);
|
||||
this.splice(position, 0, connection);
|
||||
connection.inDB_ = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the given connection.
|
||||
* Starts by doing a binary search to find the approximate location, then
|
||||
* linearly searches nearby for the exact connection.
|
||||
* @param {Blockly.Connection} conn The connection to find.
|
||||
* @return The index of the connection, or -1 if the connection was not found.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.findConnection = function(conn) {
|
||||
if (this.length == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var bestGuess = this.findPositionForConnection_(conn);
|
||||
if (bestGuess >= this.length) {
|
||||
// Not in list
|
||||
return -1;
|
||||
}
|
||||
|
||||
var yPos = conn.y_;
|
||||
// Walk forward and back on the y axis looking for the connection.
|
||||
var pointerMin = bestGuess;
|
||||
var pointerMax = bestGuess;
|
||||
while(pointerMin >= 0 && this[pointerMin].y_ == yPos) {
|
||||
if (this[pointerMin] == conn) {
|
||||
return pointerMin;
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
|
||||
while (pointerMax < this.length && this[pointerMax].y_ == yPos) {
|
||||
if (this[pointerMax] == conn) {
|
||||
return pointerMax;
|
||||
}
|
||||
pointerMax++;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a candidate position for inserting this connection into the list.
|
||||
* This will be in the correct y order but makes no guarantees about ordering in
|
||||
* the x axis.
|
||||
* @param {Blockly.Connection} connection The connection to insert.
|
||||
* @return {number} The candidate index.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.findPositionForConnection_ = function(connection) {
|
||||
if (this.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length;
|
||||
while (pointerMin < pointerMax) {
|
||||
var pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid + 1;
|
||||
} else if (this[pointerMid].y_ > connection.y_) {
|
||||
pointerMax = pointerMid;
|
||||
} else {
|
||||
pointerMin = pointerMid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pointerMin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a connection from the database. Must already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be removed.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) {
|
||||
if (!connection.inDB_) {
|
||||
throw 'Connection not in database.';
|
||||
}
|
||||
var removalIndex = this.findConnection(connection);
|
||||
if (removalIndex == -1) {
|
||||
throw 'Unable to find connection in connectionDB.';
|
||||
}
|
||||
connection.inDB_ = false;
|
||||
this.splice(removalIndex, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find all nearby connections to the given connection.
|
||||
* Type checking does not apply, since this function is used for bumping.
|
||||
* @param {!Blockly.Connection} connection The connection whose neighbours should
|
||||
* be returned.
|
||||
* @param {number} maxRadius The maximum radius to another connection.
|
||||
* @return {!Array.<Blockly.Connection>} List of connections.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
|
||||
var db = this;
|
||||
var currentX = connection.x_;
|
||||
var currentY = connection.y_;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var neighbours = [];
|
||||
var sourceBlock = connection.sourceBlock_;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the current connection's vertical distance from
|
||||
* the other connection is less than the allowed radius.
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var dx = currentX - db[yIndex].x_;
|
||||
var dy = currentY - db[yIndex].y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxRadius) {
|
||||
neighbours.push(db[yIndex]);
|
||||
}
|
||||
return dy < maxRadius;
|
||||
}
|
||||
return neighbours;
|
||||
};
|
||||
|
||||
|
||||
Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) {
|
||||
return (Math.abs(this[index].y_ - baseY) <= maxRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest compatible connection to this connection.
|
||||
* @param {Blockly.Connection} conn The connection searching for a compatible
|
||||
* mate.
|
||||
* @param {number} maxRadius The maximum radius to another connection.
|
||||
* @param {number} dx Horizontal offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @param {number} dy Vertical offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @return ?Blockly.Connection the closest valid connection.
|
||||
* another connection or null, and 'radius' which is the distance.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dx,
|
||||
dy) {
|
||||
// Don't bother.
|
||||
if (!this.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Stash the values of x and y from before the drag.
|
||||
var baseY = conn.y_;
|
||||
var baseX = conn.x_;
|
||||
|
||||
conn.x_ = baseX + dx;
|
||||
conn.y_ = baseY + dy;
|
||||
|
||||
// findPositionForConnection finds an index for insertion, which is always
|
||||
// after any block with the same y index. We want to search both forward
|
||||
// and back, so search on both sides of the index.
|
||||
var closestIndex = this.findPositionForConnection_(conn);
|
||||
|
||||
var bestConnection = null;
|
||||
var bestRadius = maxRadius;
|
||||
var temp;
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
var pointerMin = closestIndex - 1;
|
||||
while (pointerMin >= 0 &&
|
||||
this.isInYRange_(pointerMin, conn.y_, maxRadius)) {
|
||||
temp = this[pointerMin];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
|
||||
var pointerMax = closestIndex;
|
||||
while (pointerMax < this.length && this.isInYRange_(pointerMax, conn.y_,
|
||||
maxRadius)) {
|
||||
temp = this[pointerMax];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
pointerMax++;
|
||||
}
|
||||
|
||||
// Reset the values of x and y.
|
||||
conn.x_ = baseX;
|
||||
conn.y_ = baseY;
|
||||
return bestConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a set of connection DBs for a specified workspace.
|
||||
* @param {!Blockly.Workspace} workspace The workspace this DB is for.
|
||||
*/
|
||||
Blockly.ConnectionDB.init = function(workspace) {
|
||||
// Create four databases, one for each connection type.
|
||||
var dbList = [];
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB();
|
||||
workspace.connectionDBList = dbList;
|
||||
};
|
||||
@@ -39,23 +39,23 @@ function verify_DB_(msg, expected, db) {
|
||||
function test_DB_addConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var o2 = {y_: 2, sourceBlock_: {}};
|
||||
db.addConnection_(o2);
|
||||
db.addConnection(o2);
|
||||
verify_DB_('Adding connection #2', [o2], db);
|
||||
|
||||
var o4 = {y_: 4, sourceBlock_: {}};
|
||||
db.addConnection_(o4);
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connection #4', [o2, o4], db);
|
||||
|
||||
var o1 = {y_: 1, sourceBlock_: {}};
|
||||
db.addConnection_(o1);
|
||||
db.addConnection(o1);
|
||||
verify_DB_('Adding connection #1', [o1, o2, o4], db);
|
||||
|
||||
var o3a = {y_: 3, sourceBlock_: {}};
|
||||
db.addConnection_(o3a);
|
||||
db.addConnection(o3a);
|
||||
verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db);
|
||||
|
||||
var o3b = {y_: 3, sourceBlock_: {}};
|
||||
db.addConnection_(o3b);
|
||||
db.addConnection(o3b);
|
||||
verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db);
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ function test_DB_removeConnection() {
|
||||
var o3b = {y_: 3, sourceBlock_: {}};
|
||||
var o3c = {y_: 3, sourceBlock_: {}};
|
||||
var o4 = {y_: 4, sourceBlock_: {}};
|
||||
db.addConnection_(o1);
|
||||
db.addConnection_(o2);
|
||||
db.addConnection_(o3c);
|
||||
db.addConnection_(o3b);
|
||||
db.addConnection_(o3a);
|
||||
db.addConnection_(o4);
|
||||
db.addConnection(o1);
|
||||
db.addConnection(o2);
|
||||
db.addConnection(o3c);
|
||||
db.addConnection(o3b);
|
||||
db.addConnection(o3a);
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db);
|
||||
|
||||
db.removeConnection_(o2);
|
||||
@@ -93,3 +93,190 @@ function test_DB_removeConnection() {
|
||||
db.removeConnection_(o3b);
|
||||
verify_DB_('Removing connection #3b', [], db);
|
||||
}
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db,
|
||||
10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning.
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle.
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end.
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction.
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction.
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally.
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function test_DB_findPositionForConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
db.addConnection(helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 1, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 2, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 4, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 5, Blockly.PREVIOUS_STATEMENT));
|
||||
|
||||
assertEquals(5, db.length);
|
||||
var conn = helper_createConnection(0, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
assertEquals(3, db.findPositionForConnection_(conn));
|
||||
}
|
||||
|
||||
function test_DB_findConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(i, 0,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
var conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
db.addConnection(conn);
|
||||
assertEquals(conn, db[db.findConnection(conn)]);
|
||||
|
||||
conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
assertEquals(-1, db.findConnection(conn));
|
||||
}
|
||||
|
||||
function test_DB_ordering() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, 9 - i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
assertEquals(i, db[i].y_);
|
||||
}
|
||||
|
||||
// quasi-random
|
||||
var xCoords = [-29, -47, -77, 2, 43, 34, -59, -52, -90, -36, -91, 38, 87, -20,
|
||||
60, 4, -57, 65, -37, -81, 57, 58, -96, 1, 67, -79, 34, 93, -90, -99, -62,
|
||||
4, 11, -36, -51, -72, 3, -50, -24, -45, -92, -38, 37, 24, -47, -73, 79,
|
||||
-20, 99, 43, -10, -87, 19, 35, -62, -36, 49, 86, -24, -47, -89, 33, -44,
|
||||
25, -73, -91, 85, 6, 0, 89, -94, 36, -35, 84, -9, 96, -21, 52, 10, -95, 7,
|
||||
-67, -70, 62, 9, -40, -95, -9, -94, 55, 57, -96, 55, 8, -48, -57, -87, 81,
|
||||
23, 65];
|
||||
var yCoords = [-81, 82, 5, 47, 30, 57, -12, 28, 38, 92, -25, -20, 23, -51, 73,
|
||||
-90, 8, 28, -51, -15, 81, -60, -6, -16, 77, -62, -42, -24, 35, 95, -46,
|
||||
-7, 61, -16, 14, 91, 57, -38, 27, -39, 92, 47, -98, 11, -33, -72, 64, 38,
|
||||
-64, -88, -35, -59, -76, -94, 45, -25, -100, -95, 63, -97, 45, 98, 99, 34,
|
||||
27, 52, -18, -45, 66, -32, -38, 70, -73, -23, 5, -2, -13, -9, 48, 74, -97,
|
||||
-11, 35, -79, -16, -77, 83, -57, -53, 35, -44, 100, -27, -15, 5, 39, 33,
|
||||
-19, -20, -95];
|
||||
for (i = 0; i < xCoords.length; i++) {
|
||||
db.addConnection(helper_createConnection(xCoords[i], yCoords[i],
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
for (i = 1; i < xCoords.length; i++) {
|
||||
assertTrue(db[i].y_ >= db[i - 1].y_);
|
||||
}
|
||||
}
|
||||
|
||||
function test_SearchForClosest() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var sharedWorkspace = {id: "Shared workspace"};
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(null, helper_searchDB(db, 10 /* x */, 10 /* y */,
|
||||
100 /* radius */));
|
||||
|
||||
db.addConnection(helper_createConnection(100, 0, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace));
|
||||
assertEquals(null, helper_searchDB(db, 0, 0, 5, sharedWorkspace));
|
||||
|
||||
db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var tempConn = helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
}
|
||||
|
||||
// Should be at 0, 9.
|
||||
var last = db[db.length - 1];
|
||||
// Correct connection is last in db; many connections in radius.
|
||||
assertEquals(last, helper_searchDB(db, 0, 10, 15, sharedWorkspace));
|
||||
// Nothing nearby.
|
||||
assertEquals(null, helper_searchDB(db, 100, 100, 3, sharedWorkspace));
|
||||
// First in db, exact match.
|
||||
assertEquals(db[0], helper_searchDB(db, 0, 0, 0, sharedWorkspace));
|
||||
|
||||
tempConn = helper_createConnection(6, 6, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
tempConn = helper_createConnection(5, 5, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
|
||||
var result = helper_searchDB(db, 4, 6, 3, sharedWorkspace);
|
||||
assertEquals(5, result.x_);
|
||||
assertEquals(5, result.y_);
|
||||
}
|
||||
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT),
|
||||
radius);
|
||||
}
|
||||
|
||||
function helper_searchDB(db, x, y, radius, shared_workspace) {
|
||||
var tempConn = helper_createConnection(x, y,
|
||||
Blockly.NEXT_STATEMENT, shared_workspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(shared_workspace);
|
||||
return db.searchForClosest(tempConn, radius, 0, 0);
|
||||
}
|
||||
|
||||
function helper_makeSourceBlock(sharedWorkspace) {
|
||||
return {workspace: sharedWorkspace,
|
||||
parentBlock_: null,
|
||||
getParent: function() { return null; },
|
||||
movable_: true,
|
||||
isMovable: function() { return true; },
|
||||
isShadow: function() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type, opt_shared_workspace) {
|
||||
var workspace = opt_shared_workspace ? opt_shared_workspace : {};
|
||||
var conn = new Blockly.Connection({workspace: workspace}, type);
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
||||
@@ -33,10 +33,14 @@ var dummyWorkspace;
|
||||
|
||||
function connectionTest_setUp() {
|
||||
dummyWorkspace = {};
|
||||
input = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
previous = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.PREVIOUS_STATEMENT);
|
||||
next = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.NEXT_STATEMENT);
|
||||
input = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
previous = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.PREVIOUS_STATEMENT);
|
||||
next = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.NEXT_STATEMENT);
|
||||
}
|
||||
|
||||
function connectionTest_tearDown() {
|
||||
@@ -48,13 +52,14 @@ function connectionTest_tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* These tests check that the reasons for failures to connect are consistent (internal view of
|
||||
* error states).
|
||||
* These tests check that the reasons for failures to connect are consistent
|
||||
* (internal view of error states).
|
||||
*/
|
||||
function testCanConnectWithReason_TargetNull() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_TARGET_NULL, input.canConnectWithReason_(null));
|
||||
assertEquals(Blockly.Connection.REASON_TARGET_NULL,
|
||||
input.canConnectWithReason_(null));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
@@ -62,9 +67,11 @@ function testCanConnectWithReason_TargetNull() {
|
||||
function testCanConnectWithReason_Disconnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
Blockly.Connection.connectReciprocally(input, tempConnection);
|
||||
assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
@@ -73,9 +80,11 @@ function testCanConnectWithReason_DifferentWorkspaces() {
|
||||
connectionTest_setUp();
|
||||
|
||||
input = new Blockly.Connection({workspace: {}}, Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
@@ -86,7 +95,8 @@ function testCanConnectWithReason_Self() {
|
||||
|
||||
var block = {type_: "test block"};
|
||||
input.sourceBlock_ = block;
|
||||
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION, input.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION,
|
||||
input.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
@@ -94,17 +104,25 @@ function testCanConnectWithReason_Self() {
|
||||
function testCanConnectWithReason_Type() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(output));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
@@ -112,17 +130,21 @@ function testCanConnectWithReason_Type() {
|
||||
function testCanConnectWithReason_CanConnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, previous.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, next.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, output.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
previous.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
next.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
output.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* The next set of tests checks that exceptions are being thrown at the correct times (external
|
||||
* view of errors).
|
||||
* The next set of tests checks that exceptions are being thrown at the correct
|
||||
* times (external view of errors).
|
||||
*/
|
||||
function testCheckConnection_Self() {
|
||||
connectionTest_setUp();
|
||||
@@ -222,6 +244,40 @@ function testCheckConnection_TypeNextOutput() {
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed() {
|
||||
var sharedWorkspace = {};
|
||||
// Two connections of opposite types near each other.
|
||||
var one = helper_createConnection(5 /* x */, 10 /* y */,
|
||||
Blockly.INPUT_VALUE);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
var two = helper_createConnection(10 /* x */, 15 /* y */,
|
||||
Blockly.OUTPUT_VALUE);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertTrue(one.isConnectionAllowed(two, 20.0));
|
||||
// Move connections farther apart.
|
||||
two.x_ = 100;
|
||||
two.y_ = 100;
|
||||
assertFalse(one.isConnectionAllowed(two, 20.0));
|
||||
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug.
|
||||
var three = helper_createConnection(0, 0, Blockly.OUTPUT_VALUE);
|
||||
three.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertTrue(one.isConnectionAllowed(three, 20.0));
|
||||
var four = helper_createConnection(0, 0, Blockly.INPUT_VALUE);
|
||||
four.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
Blockly.Connection.connectReciprocally(three, four);
|
||||
assertFalse(one.isConnectionAllowed(three, 20.0));
|
||||
|
||||
// Don't connect two connections on the same block.
|
||||
two.sourceBlock_ = one.sourceBlock_;
|
||||
assertFalse(one.isConnectionAllowed(two, 1000.0));
|
||||
}
|
||||
|
||||
function testCheckConnection_Okay() {
|
||||
connectionTest_setUp();
|
||||
previous.checkConnection_(next);
|
||||
|
||||
76
tests/jsunit/db_test.js
Normal file
76
tests/jsunit/db_test.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db, 10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection_(helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT), radius);
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type) {
|
||||
var conn = new Blockly.Connection({workspace: {}}, type);
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
||||
Reference in New Issue
Block a user