-
Notifications
You must be signed in to change notification settings - Fork 599
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #286 from ivmartel/284-draw-controller
DrawController. Fixes #284.
- Loading branch information
Showing
12 changed files
with
476 additions
and
311 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,361 @@ | ||
// namespaces | ||
var dwv = dwv || {}; | ||
// external | ||
var Kinetic = Kinetic || {}; | ||
|
||
/** | ||
* Draw controller. | ||
* @constructor | ||
* @param {Object} drawDiv The HTML div used to store the drawings. | ||
*/ | ||
dwv.DrawController = function (drawDiv) | ||
{ | ||
|
||
// Draw stage | ||
var drawStage = null; | ||
// Draw layers: 2 dimension array: [slice][frame] | ||
var drawLayers = []; | ||
|
||
// current slice position | ||
var currentSlice = 0; | ||
// current frame position | ||
var currentFrame = 0; | ||
|
||
/** | ||
* Create the controller: sets up the draw stage. | ||
* @param {Number} width The width of the stage. | ||
* @param {Number} height The height of the stage. | ||
*/ | ||
this.create = function (width, height) { | ||
// create stage | ||
drawStage = new Kinetic.Stage({ | ||
'container': drawDiv, | ||
'width': width, | ||
'height': height, | ||
'listening': false | ||
}); | ||
// reset style | ||
// (avoids a not needed vertical scrollbar) | ||
drawStage.getContent().setAttribute("style", ""); | ||
}; | ||
|
||
/** | ||
* Get the current draw layer. | ||
* @return {Object} The draw layer. | ||
*/ | ||
this.getCurrentDrawLayer = function () { | ||
//return this.getDrawLayer(currentSlice, currentFrame); | ||
return drawLayers[currentSlice][currentFrame]; | ||
}; | ||
|
||
/** | ||
* Reset: clear the layers array. | ||
*/ | ||
this.reset = function () { | ||
drawLayers = []; | ||
}; | ||
|
||
/** | ||
* Get the draw stage. | ||
* @return {Object} The draw layer. | ||
*/ | ||
this.getDrawStage = function () { | ||
return drawStage; | ||
}; | ||
|
||
/** | ||
* Activate the current draw layer. | ||
* @param {Object} viewController The associated view controller. | ||
*/ | ||
this.activateDrawLayer = function (viewController) | ||
{ | ||
// hide all draw layers | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
drawLayers[k][f].visible( false ); | ||
} | ||
} | ||
// set current position | ||
currentSlice = viewController.getCurrentPosition().k; | ||
currentFrame = viewController.getCurrentFrame(); | ||
// show current draw layer | ||
var currentLayer = this.getCurrentDrawLayer(); | ||
currentLayer.visible( true ); | ||
currentLayer.draw(); | ||
}; | ||
|
||
/** | ||
* Reset the stage with a new window scale. | ||
* @param {Number} windowScale The window scale. | ||
*/ | ||
this.resetStage = function (windowScale) { | ||
drawStage.offset( {'x': 0, 'y': 0} ); | ||
drawStage.scale( {'x': windowScale, 'y': windowScale} ); | ||
drawStage.draw(); | ||
}; | ||
|
||
/** | ||
* Resize the current stage. | ||
* @param {Number} width the stage width. | ||
* @param {Number} height the stage height. | ||
* @param {Number} scale the stage scale. | ||
*/ | ||
this.resizeStage = function (width, height, scale) { | ||
// resize div | ||
drawDiv.setAttribute("style","width:"+width+"px;height:"+height+"px"); | ||
// resize stage | ||
drawStage.setWidth(width); | ||
drawStage.setHeight(height); | ||
drawStage.scale( {'x': scale, 'y': scale} ); | ||
drawStage.draw(); | ||
}; | ||
|
||
/** | ||
* Zoom the stage. | ||
* @param {Number} scale The scale factor. | ||
* @param {Object} scaleCenter The scale center point. | ||
*/ | ||
this.zoomStage = function (scale, scaleCenter) { | ||
// zoom | ||
var newScale = {'x': scale, 'y': scale}; | ||
// offset | ||
// TODO different from the imageLayer offset? | ||
var oldScale = drawStage.scale(); | ||
var oldOffset = drawStage.offset(); | ||
var newOffsetX = (scaleCenter.x / oldScale.x) + | ||
oldOffset.x - (scaleCenter.x / newScale.x); | ||
var newOffsetY = (scaleCenter.y / oldScale.y) + | ||
oldOffset.y - (scaleCenter.y / newScale.y); | ||
var newOffset = {'x': newOffsetX, 'y': newOffsetY}; | ||
// store | ||
drawStage.offset( newOffset ); | ||
drawStage.scale( newScale ); | ||
drawStage.draw(); | ||
}; | ||
|
||
/** | ||
* Translate the stage. | ||
* @param {Number} tx The X translation. | ||
* @param {Number} ty The Y translation. | ||
*/ | ||
this.translateStage = function (tx, ty) { | ||
drawStage.offset( {'x': tx, 'y': ty} ); | ||
drawStage.draw(); | ||
}; | ||
|
||
/** | ||
* Append a new draw layer list to the list. | ||
* @param {Number} nLayers The size of the layers array to append to the current one. | ||
*/ | ||
this.appendDrawLayer = function (nLayers) { | ||
// add a new dimension | ||
drawLayers.push([]); | ||
// fill it | ||
for (var i = 0; i < nLayers; ++i) { | ||
// create draw layer | ||
var drawLayer = new Kinetic.Layer({ | ||
'listening': false, | ||
'hitGraphEnabled': false, | ||
'visible': false | ||
}); | ||
drawLayers[drawLayers.length - 1].push(drawLayer); | ||
// add the layer to the stage | ||
drawStage.add(drawLayer); | ||
} | ||
}; | ||
|
||
/** | ||
* Get a list of drawing display details. | ||
* @return {Object} A list of draw details including id, slice, frame... | ||
*/ | ||
this.getDrawDisplayDetails = function () | ||
{ | ||
var list = []; | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
var collec = drawLayers[k][f].getChildren(); | ||
for ( var i = 0, leni = collec.length; i < leni; ++i ) { | ||
var shape = collec[i].getChildren()[0]; | ||
var label = collec[i].getChildren()[1]; | ||
var text = label.getChildren()[0]; | ||
var type = shape.className; | ||
if (type === "Line" && shape.closed()) { | ||
type = "Roi"; | ||
} | ||
if (type === "Rect") { | ||
type = "Rectangle"; | ||
} | ||
list.push( { | ||
"id": collec[i].id(), | ||
"slice": k, | ||
"frame": f, | ||
"type": type, | ||
"color": shape.stroke(), | ||
"label": text.textExpr, | ||
"description": text.longText | ||
}); | ||
} | ||
} | ||
} | ||
// return | ||
return list; | ||
}; | ||
|
||
/** | ||
* Get all the draws of the stage. | ||
*/ | ||
this.getDraws = function () | ||
{ | ||
var drawGroups = []; | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
drawGroups[k] = []; | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
// getChildren always return, so drawings will have the good size | ||
var groups = drawLayers[k][f].getChildren(); | ||
drawGroups[k].push(groups); | ||
} | ||
} | ||
return drawGroups; | ||
}; | ||
|
||
/** | ||
* Get a list of drawing store details. | ||
* @return {Object} A list of draw details including id, text, quant... | ||
* TODO Unify with getDrawDisplayDetails? | ||
*/ | ||
this.getDrawStoreDetails = function () | ||
{ | ||
var drawingsDetails = []; | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
drawingsDetails[k] = []; | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
// getChildren always return, so drawings will have the good size | ||
var groups = drawLayers[k][f].getChildren(); | ||
var details = []; | ||
for ( var i = 0, leni = groups.length; i < leni; ++i ) { | ||
// remove anchors | ||
var anchors = groups[i].find(".anchor"); | ||
for ( var a = 0; a < anchors.length; ++a ) { | ||
anchors[a].remove(); | ||
} | ||
// get text | ||
var texts = groups[i].find(".text"); | ||
if ( texts.length !== 1 ) { | ||
console.warn("There should not be more than one text per shape."); | ||
} | ||
// get details (non Kinetic vars) | ||
details.push({ | ||
"id": groups[i].id(), | ||
"textExpr": encodeURIComponent(texts[0].textExpr), | ||
"longText": encodeURIComponent(texts[0].longText), | ||
"quant": texts[0].quant | ||
}); | ||
} | ||
drawingsDetails[k].push(details); | ||
} | ||
} | ||
return drawingsDetails; | ||
}; | ||
|
||
/** | ||
* Set the drawings on the current stage. | ||
* @param {Array} drawings An array of drawings. | ||
* @param {Array} drawingsDetails An array of drawings details. | ||
* @param {Object} cmdCallback The DrawCommand callback. | ||
* @param {Object} exeCallback The callback to call once the DrawCommand has been executed. | ||
*/ | ||
this.setDrawings = function (drawings, drawingsDetails, cmdCallback, exeCallback) | ||
{ | ||
// Is an input node a 'shape' | ||
var isShape = function (node) { | ||
return node.name() === "shape"; | ||
}; | ||
// Is an input node a 'label' | ||
var isLabel = function (node) { | ||
return node.name() === "label"; | ||
}; | ||
// loop through layers | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
for ( var i = 0, leni = drawings[k][f].length; i < leni; ++i ) { | ||
// create the group | ||
var group = Kinetic.Node.create(drawings[k][f][i]); | ||
var shape = group.getChildren( isShape )[0]; | ||
// create the draw command | ||
var cmd = new dwv.tool.DrawGroupCommand( | ||
group, shape.className, | ||
drawLayers[k][f] ); | ||
// draw command callbacks | ||
cmd.onExecute = cmdCallback; | ||
cmd.onUndo = cmdCallback; | ||
// text (new in v0.2) | ||
// TODO Verify ID? | ||
if (drawingsDetails) { | ||
var details = drawingsDetails[k][f][i]; | ||
var label = group.getChildren( isLabel )[0]; | ||
var text = label.getText(); | ||
// store details | ||
text.textExpr = details.textExpr; | ||
text.longText = details.longText; | ||
text.quant = details.quant; | ||
// reset text (it was not encoded) | ||
text.setText(dwv.utils.replaceFlags(text.textExpr, text.quant)); | ||
} | ||
// execute | ||
cmd.execute(); | ||
exeCallback(cmd); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Update a drawing from its details. | ||
* @param {Object} drawDetails Details of the drawing to update. | ||
*/ | ||
this.updateDraw = function (drawDetails) | ||
{ | ||
var layer = drawLayers[drawDetails.slice][drawDetails.frame]; | ||
//var collec = layer.getChildren()[drawDetails.id]; | ||
var collec = layer.getChildren( function (node) { | ||
return node.id() === drawDetails.id; | ||
})[0]; | ||
// shape | ||
var shape = collec.getChildren()[0]; | ||
shape.stroke(drawDetails.color); | ||
// label | ||
var label = collec.getChildren()[1]; | ||
var text = label.getChildren()[0]; | ||
text.fill(drawDetails.color); | ||
text.textExpr = drawDetails.label; | ||
text.longText = drawDetails.description; | ||
text.setText(dwv.utils.replaceFlags(text.textExpr, text.quant)); | ||
|
||
// udpate current layer | ||
this.getCurrentDrawLayer().draw(); | ||
}; | ||
|
||
/** | ||
* Delete all Draws from the stage. | ||
* @param {Object} cmdCallback The DeleteCommand callback. | ||
* @param {Object} exeCallback The callback to call once the DeleteCommand has been executed. | ||
*/ | ||
this.deleteDraws = function (cmdCallback, exeCallback) { | ||
var delcmd, layer, groups; | ||
for ( var k = 0, lenk = drawLayers.length; k < lenk; ++k ) { | ||
for ( var f = 0, lenf = drawLayers[k].length; f < lenf; ++f ) { | ||
layer = drawLayers[k][f]; | ||
groups = layer.getChildren(); | ||
while (groups.length) { | ||
var shape = groups[0].getChildren()[0]; | ||
delcmd = new dwv.tool.DeleteGroupCommand( groups[0], | ||
dwv.tool.GetShapeDisplayName(shape), layer); | ||
delcmd.onExecute = cmdCallback; | ||
delcmd.execute(); | ||
exeCallback(delcmd); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
}; // class dwv.DrawController |
Oops, something went wrong.