<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <!--<script type="text/javascript" charset="utf-8" src="go.js"></script>-->
    <!--<script type="text/javascript" charset="utf-8" src="http://rawgit.com/NorthwoodsSoftware/GoJS/master/release/go.js"></script>-->
    
    <script type="text/javascript" charset="utf-8" src="https://gojs.net/beta/release/go.js"></script>

    <script src="GeometryReshapingTool.js"></script>
    <script src="FreehandDrawingTool.js"></script>
    <script src="script.js"></script>

  </head>

  <body onload="init()">
    <div id="sample">
      <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 600px"></div>
      <div id="buttons">
        <button onclick="mode(false)">Select</button>
        <button onclick="mode(true)">Draw Mode</button>
        <button onclick="save()">Save</button>
        <button onclick="load()">Load</button>
        <label><input type="checkbox" onclick="myDiagram.allowResize = !myDiagram.allowResize; updateAllAdornments()" checked="checked" />Allow Resizing</label>
        <label><input type="checkbox" onclick="myDiagram.allowReshape = !myDiagram.allowReshape; updateAllAdornments()" checked="checked" />Allow Reshaping</label>
        <label><input type="checkbox" onclick="myDiagram.allowRotate = !myDiagram.allowRotate; updateAllAdornments()" checked="checked" />Allow Rotating</label>
      </div>
      <p>
        This sample demonstrates the FreehandDrawingTool. It is defined in its own file, as <a href="FreehandDrawingTool.js">FreehandDrawingTool.js</a>.
        It also demonstrates the GeometryReshapingTool, another custom tool, defined in <a href="GeometryReshapingTool.js">GeometryReshapingTool.js</a>.
      </p>
      <p>
        Press and drag to draw a line.
      </p>
      <p>
        Click the "Select" button to switch back to the normal selection behavior, so that you can select, resize, and rotate the shapes.
        The checkboxes control whether you can resize, reshape, and/or rotate selected shapes.
      </p>
      <textarea id="mySavedDiagram" style="width:100%;height:300px">
    { "position": "0 0",
      "model": { "class": "go.GraphLinksModel",
      "nodeDataArray": [ {"loc":"301 143", "category":"FreehandDrawing", "geo":"M0 70 L1 70 L2 70 L3 70 L5 70 L7 70 L8 70 L11 70 L13 70 L18 69 L21 69 L25 68 L29 67 L34 67 L38 67 L42 67 L47 66 L50 66 L53 66 L55 66 L57 66 L60 66 L63 66 L64 66 L66 66 L68 66 L70 66 L72 66 L74 66 L76 66 L78 65 L81 65 L83 65 L85 65 L88 65 L90 65 L92 65 L95 65 L98 65 L100 65 L102 65 L104 65 L106 65 L109 65 L110 65 L111 65 L112 65 L113 65 L114 65 L115 65 L116 65 L118 65 L119 65 L120 65 L121 65 L122 65 L123 65 L124 65 L125 65 L126 65 L127 65 L128 65 L129 65 L131 65 L131 64 L132 64 L133 64 L134 64 L135 64 L137 64 L138 64 L139 64 L140 64 L141 64 L140 64 L139 64 L138 64 L137 64 L135 65 L134 65 L132 66 L130 67 L129 67 L126 68 L123 70 L121 71 L119 72 L116 73 L114 74 L111 76 L109 77 L106 78 L104 79 L99 81 L96 84 L94 84 L90 87 L89 87 L87 88 L86 89 L84 89 L83 90 L81 91 L80 92 L79 92 L77 93 L76 94 L74 94 L74 95 L73 96 L71 96 L70 97 L68 98 L67 98 L66 99 L64 99 L64 100 L62 100 L61 101 L60 102 L59 102 L58 103 L57 103 L57 104 L56 104 L56 105 L54 105 L54 107 L53 107 L52 108 L51 108 L51 109 L49 110 L48 111 L47 112 L47 113 L46 113 L45 114 L44 114 L44 115 L44 116 L43 116 L43 117 L42 117 L42 119 L41 119 L41 120 L40 120 L40 121 L39 122 L39 123 L38 124 L37 125 L36 126 L36 127 L35 128 L34 128 L34 129 L33 130 L33 131 L32 131 L32 130 L32 129 L33 128 L34 125 L35 122 L37 119 L39 115 L41 111 L41 106 L43 103 L45 98 L47 93 L48 90 L49 86 L51 83 L52 81 L54 78 L55 74 L55 71 L56 68 L57 65 L58 62 L58 59 L58 55 L58 53 L58 51 L58 49 L58 48 L59 45 L60 44 L60 42 L61 40 L61 38 L62 36 L64 32 L64 30 L65 29 L66 27 L66 26 L66 25 L66 24 L67 23 L67 22 L67 21 L67 20 L67 19 L68 19 L68 18 L68 17 L69 16 L69 15 L69 14 L69 12 L69 11 L69 10 L70 9 L70 8 L70 7 L70 6 L71 5 L71 4 L71 3 L71 2 L71 1 L71 0 L71 1 L71 2 L71 5 L71 6 L71 8 L71 9 L71 12 L71 14 L72 16 L72 18 L73 20 L73 23 L74 25 L74 27 L75 29 L76 32 L77 34 L78 35 L79 38 L79 39 L81 42 L81 43 L82 45 L83 46 L83 47 L83 49 L85 50 L86 52 L86 55 L86 58 L88 60 L89 62 L89 64 L90 66 L91 67 L91 69 L92 70 L92 71 L93 72 L94 73 L94 74 L94 75 L95 76 L96 77 L96 79 L97 81 L98 82 L98 83 L98 84 L99 85 L99 86 L100 87 L100 90 L101 91 L102 92 L102 93 L103 95 L103 96 L103 97 L104 98 L104 100 L104 101 L105 102 L106 103 L106 104 L107 106 L108 108 L109 110 L110 111 L111 113 L112 114 L113 115 L113 117 L115 119 L116 121 L116 123 L118 124 L119 126 L120 127 L121 129 L121 130 L122 131 L123 131 L123 132 L124 132 L124 133 L125 134 L125 135 L126 135 L127 136 L128 137 L129 138 L130 139 L131 140 L130 139 L129 138 L128 138 L126 136 L124 136 L123 134 L121 133 L118 131 L116 130 L114 128 L111 127 L107 123 L105 122 L102 120 L100 119 L98 117 L95 116 L93 114 L90 113 L88 111 L85 110 L83 108 L81 106 L78 105 L77 104 L75 103 L72 102 L71 101 L70 100 L68 99 L67 98 L65 98 L64 97 L62 96 L60 96 L58 95 L57 94 L54 93 L53 93 L51 92 L49 91 L48 91 L47 91 L45 90 L44 90 L43 89 L42 89 L41 89 L40 88 L39 87 L37 87 L36 86 L35 85 L33 85 L32 84 L31 84 L30 83 L29 83 L28 82 L26 81 L25 81 L24 80 L22 80 L21 79 L21 78 L20 78 L19 78 L18 77 L17 77 L16 76 L15 76 L15 75 L14 75 L13 75 L12 74 L11 74 L10 74 L9 74 L7 73 L5 72 L4 72 L3 72 L2 71", "key":-1} ],
      "linkDataArray": [  ]
    } }
      </textarea>
    </div>
  </body>

</html>
function init() {
    if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;
    myDiagram =
      $(go.Diagram, "myDiagramDiv");
    myDiagram.toolManager.mouseDownTools.insertAt(3, new GeometryReshapingTool());
    
    const FreehandDrawingTemplate = $(go.Part,
        { locationSpot: go.Spot.Center, isLayoutPositioned: false, name: 'nodeTemplate',
/*            click: (e, obj) => {
              debugger
            }*/
          
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        { reshapable: true, zOrder: 1 },  // GeometryReshapingTool assumes nonexistent Part.reshapeObjectName would be "SHAPE"
        {
          selectionAdorned: true, selectionObjectName: "SHAPE",
          selectionAdornmentTemplate:  // custom selection adornment: a blue rectangle
            $(go.Adornment, go.Panel.Spot, 
              {
                layerName: 'LayerAfterTool'
              },
              $(go.Panel, 'Auto',
                $(go.Shape, { stroke: "dodgerblue", fill: null }),
                $(go.Placeholder, { margin: -1 })
              ),
              $(go.Panel, 'Horizontal',
                  {
                      // isPanelMain: true,
                      // alignmentFocus: go.Spot.Bottom,
                      alignmentFocus: go.Spot.Top,
                      alignment: new go.Spot(0.5, 0, 0, -35),
                      background: '#551199'
                  },
                  $(go.Panel, 'Vertical',
                      {
                          alignment: go.Spot.Top,
                      },
          
                      $('PanelExpanderButton', 'ttt'),
                      $(go.Panel, 'Vertical',
                          {
                              visible: false,
                              name: 'ttt',
                              width: 60,
                              height: 120,
                              background: '#ff0000'
                          },
                      ),
                  ),
              )
            ),
          zOrder: 2
        },
        { rotatable: true, rotateObjectName: "WRAPPER", zOrder: 3 },
        { resizable: true, resizeObjectName: "WRAPPER", zOrder: 4 },
        $(go.Panel, 'Auto',
          {
            name: 'WRAPPER'
          },
          $(go.Shape,
            { 
              name: "SHAPE",
              fill: null,
              strokeWidth: 25,
              stretch: go.GraphObject.Fill,
              // stretch: go.GraphObject.Vertical,
              // background: 'green',
  
              
            },
            new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
            new go.Binding("angle").makeTwoWay(),
            new go.Binding("geometryString", "geo").makeTwoWay(),
            new go.Binding("fill"),
            new go.Binding("stroke"),
            new go.Binding("strokeWidth")
        )
        ),


      )
      
        // FreehandDrawingTemplate
        // debugger
      
    myDiagram.nodeTemplateMap.add("FreehandDrawing", FreehandDrawingTemplate);
    // create drawing tool for myDiagram, defined in FreehandDrawingTool.js
    var tool = new FreehandDrawingTool();
    // provide the default JavaScript object for a new polygon in the model
    tool.archetypePartData =
      { stroke: "green", strokeWidth: 3, category: "FreehandDrawing" };
    // allow the tool to start on top of an existing Part
    tool.isBackgroundOnly = false;
    // install as first mouse-move-tool
    myDiagram.toolManager.mouseMoveTools.insertAt(0, tool);
    

    /**
     * "Grid",
     * "Background",
     * "" (the default layer),
     * "Foreground",
     * "Adornment",
     * "Tool"
     */
    
    var Foregroundlayer = myDiagram.findLayer("Foreground");
      myDiagram.addLayerBefore($(go.Layer, { name: "LayerBeforeForeground" }), Foregroundlayer);
      
      var Toollayer = myDiagram.findLayer("Tool");
      myDiagram.addLayerAfter($(go.Layer, { name: "LayerAfterTool" }), Toollayer);
    
    setTimeout(()=> {
      console.clear();
      // myDiagram.currentTool 
      //myDiagram.nodeTemplateMap
      
      
      let it = myDiagram.layers.iterator
      // debugger
      while(it.next()) {
        let layer = it.value;
        console.log('layer.name - ', layer.name);
        console.log('layer.parts.count - ', layer.parts.count);
        // debugger
        if (layer.parts.count) {
          layer.parts
          let it2 = layer.parts.iterator
          while(it2.next()) {
              let part = it2.value;
              
              /**
               * Selection
               * GeometryReshaping
               * Resizing
               * Rotating
               * 
               */
              
              
              console.log('part.layerName - ', part.layerName);
              console.log('part.name - ', part.name);
              debugger
          }
          
        }
        console.log('----------------------------------------')
        // debugger
      }
      

    }, 4000) 
    load();  // load a simple diagram from the textarea
  }
  function mode(draw) {
    var tool = myDiagram.toolManager.findTool("FreehandDrawing");
    tool.isEnabled = draw;
  }
  function updateAllAdornments() {  // called after checkboxes change Diagram.allow...
    myDiagram.selection.each(function(p) { p.updateAdornments(); });
  }
  // save a model to and load a model from Json text, displayed below the Diagram
  function save() {
    var str = '{ "position": "' + go.Point.stringify(myDiagram.position) + '",\n  "model": ' + myDiagram.model.toJson() + ' }';
    document.getElementById("mySavedDiagram").value = str;
  }
  function load() {
    var str = document.getElementById("mySavedDiagram").value;
    try {
      var json = JSON.parse(str);
      myDiagram.initialPosition = go.Point.parse(json.position || "0 0");
      myDiagram.model = go.Model.fromJson(json.model);
      myDiagram.model.undoManager.isEnabled = true;
    } catch (ex) {
      alert(ex);
    }
  }
/* Styles go here */

"use strict";
/*
*  Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved.
*/

// A custom Tool for freehand drawing

/**
* @constructor
* @extends Tool
* @class
* This tool allows the user to draw a shape using the mouse.
* It collects all of the points from a mouse-down, all mouse-moves, until a mouse-up,
* and puts all of those points in a {@link Geometry} used by a {@link Shape}.
* It then adds a node data object to the diagram's model.
* <p/>
* This tool may be installed as the first mouse down tool:
* <code>myDiagram.toolManager.mouseDownTools.insertAt(0, new FreehandDrawingTool());</code>
* <p/>
* The Shape used during the drawing operation can be customized by setting {@link #temporaryShape}.
* The node data added to the model can be customized by setting {@link #archetypePartData}.
*/
function FreehandDrawingTool() {
  go.Tool.call(this);
  this.name = "FreehandDrawing";
  this._archetypePartData = {}; // the data to copy for a new polyline Part
  this._isBackgroundOnly = true; // affects canStart()

  // this is the Shape that is shown during a drawing operation
  this._temporaryShape = go.GraphObject.make(go.Shape, { name: "SHAPE", fill: null, strokeWidth: 1.5 });
  // the Shape has to be inside a temporary Part that is used during the drawing operation
  go.GraphObject.make(go.Part, { layerName: "Tool" }, this._temporaryShape);
}
go.Diagram.inherit(FreehandDrawingTool, go.Tool);

/**
* Only start if the diagram is modifiable and allows insertions.
* OPTIONAL: if the user is starting in the diagram's background, not over an existing Part.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.canStart = function() {
  if (!this.isEnabled) return false;
  var diagram = this.diagram;
  if (diagram === null || diagram.isReadOnly || diagram.isModelReadOnly) return false;
  if (!diagram.allowInsert) return false;
  // don't include the following check when this tool is running modally
  if (diagram.currentTool !== this && this.isBackgroundOnly) {
    // only operates in the background, not on some Part
    var part = diagram.findPartAt(diagram.lastInput.documentPoint, true);
    if (part !== null) return false;
  }
  return true;
};

/**
* Capture the mouse and use a "crosshair" cursor.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.doActivate = function() {
  go.Tool.prototype.doActivate.call(this);
  this.diagram.isMouseCaptured = true;
  this.diagram.currentCursor = "crosshair";
};

/**
* Cleanup.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.doDeactivate = function() {
  go.Tool.prototype.doDeactivate.call(this);
  if (this.temporaryShape !== null) {
    this.diagram.remove(this.temporaryShape.part);
  }
  this.diagram.currentCursor = "";
  this.diagram.isMouseCaptured = false;
};

/**
* This adds a Point to the {@link #temporaryShape}'s geometry.
* <p/>
* If the Shape is not yet in the Diagram, its geometry is initialized and
* its parent Part is added to the Diagram.
* <p/>
* If the point is less than half a pixel away from the previous point, it is ignored.
* @this {FreehandDrawingTool}
* @param {Point} p
*/
FreehandDrawingTool.prototype.addPoint = function(p) {
  var shape = this.temporaryShape;
  if (shape === null) return;

  // for the temporary Shape, normalize the geometry to be in the viewport
  var viewpt = this.diagram.viewportBounds.position;
  var q = new go.Point(p.x-viewpt.x, p.y-viewpt.y);

  var part = shape.part;
  if (part.diagram === null) {
    var fig = new go.PathFigure(q.x, q.y, true);  // possibly filled, depending on Shape.fill
    var geo = new go.Geometry().add(fig);  // the Shape.geometry consists of a single PathFigure
    this.temporaryShape.geometry = geo;
    // position the Shape's Part, accounting for the strokeWidth
    part.position = new go.Point(viewpt.x - shape.strokeWidth/2, viewpt.y - shape.strokeWidth/2);
    this.diagram.add(part);
  }

  // only add a point if it isn't too close to the last one
  var segs = shape.geometry.figures.first().segments;
  var idx = segs.count-1;
  if (idx >= 0) {
    var last = segs.elt(idx);
    if (Math.abs(q.x - last.endX) < 0.5 && Math.abs(q.y - last.endY) < 0.5) return;
  }

  // must copy whole Geometry in order to add a PathSegment
  var geo = shape.geometry.copy();
  var fig = geo.figures.first();
  var seg = new go.PathSegment(go.PathSegment.Line, q.x, q.y);
  fig.add(seg);
  shape.geometry = geo;
};

/**
* Start drawing the line by starting to accumulate points in the {@link #temporaryShape}'s geometry.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.doMouseDown = function() {
  if (!this.isActive) {
    this.doActivate();
    // the first point
    this.addPoint(this.diagram.lastInput.documentPoint);
  }
};

/**
* Keep accumulating points in the {@link #temporaryShape}'s geometry.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.doMouseMove = function() {
  if (this.isActive) {
    this.addPoint(this.diagram.lastInput.documentPoint);
  }
};

/**
* Finish drawing the line by adding a node data object holding the
* geometry string and the node position that the node template can bind to.
* This copies the {@link #archetypePartData} and adds it to the model.
* @this {FreehandDrawingTool}
*/
FreehandDrawingTool.prototype.doMouseUp = function() {
  var started = false;
  if (this.isActive) {
    started = true;
    var diagram = this.diagram;
    // the last point
    this.addPoint(diagram.lastInput.documentPoint);
    // normalize geometry and node position
    var viewpt = diagram.viewportBounds.position;
    var geo = this.temporaryShape.geometry.copy();
    var pos = geo.normalize();
    pos.x = viewpt.x - pos.x;
    pos.y = viewpt.y - pos.y;

    diagram.startTransaction(this.name);
    // create the node data for the model
    var d = diagram.model.copyNodeData(this.archetypePartData);
    // adding data to model creates the actual Part
    diagram.model.addNodeData(d);
    var part = diagram.findPartForData(d);
    // assign the location
    part.location = new go.Point(pos.x + geo.bounds.width/2, pos.y + geo.bounds.height/2);
    // assign the Shape.geometry
    var shape = part.findObject("SHAPE");
    if (shape !== null) shape.geometry = geo;
  }
  this.stopTool();
  if (started) diagram.commitTransaction(this.name);
};

// Public properties

/**
* Gets or sets the Shape that is used to hold the line as it is being drawn.
* The default value is a simple Shape drawing an unfilled open thin black line.
* @name FreehandDrawingTool#temporaryShape
* @function.
* @return {Shape}
*/
Object.defineProperty(FreehandDrawingTool.prototype, "temporaryShape", {
  get: function() { return this._temporaryShape; },
  set: function(val) {
    if (this._temporaryShape !== val && val !== null) {
      val.name = "SHAPE";
      var panel = this._temporaryShape.panel;
      panel.remove(this._temporaryShape);
      this._temporaryShape = val;
      panel.add(this._temporaryShape);
    }
  }
});

/**
* Gets or sets the node data object that is copied and added to the model
* when the freehand drawing operation completes.
* @name FreehandDrawingTool#archetypePartData
* @function.
* @return {Object}
*/
Object.defineProperty(FreehandDrawingTool.prototype, "archetypePartData", {
  get: function() { return this._archetypePartData; },
  set: function(val) { this._archetypePartData = val; }
});

/**
* Gets or sets whether this tool can only run if the user starts in the diagram's background
* rather than on top of an existing Part.
* The default value is true.
* @name FreehandDrawingTool#isBackgroundOnly
* @function.
* @return {Object}
*/
Object.defineProperty(FreehandDrawingTool.prototype, "isBackgroundOnly", {
  get: function() { return this._isBackgroundOnly; },
  set: function(val) { this._isBackgroundOnly = val; }
});
"use strict";
/*
*  Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved.
*/

/**
* @constructor
* @extends Tool
* @class
* This GeometryReshapingTool class allows for a Shape's Geometry to be modified by the user
* via the dragging of tool handles. 
* This does not handle Links, whose routes should be reshaped by the LinkReshapingTool.
* The {@link #reshapeObjectName} needs to identify the named {@link Shape} within the
* selected {@link Part}.
* If the shape cannot be found or if its {@link Shape#geometry} is not of type {@link Geometry#Path},
* this will not show any GeometryReshaping {@link Adornment}.
* At the current time this tool does not support adding or removing {@link PathSegment}s to the Geometry.
*/
function GeometryReshapingTool() {
	go.Tool.call(this);
	this.name = "GeometryReshaping";

	var h = new go.Shape();
  h.figure = "Diamond";
  h.desiredSize = new go.Size(7, 7);
  h.fill = "lightblue";
  h.stroke = "dodgerblue";
  h.cursor = "move";
  /** @type {GraphObject} */
	this._handleArchetype = h;

  /** @type {string} */
	this._reshapeObjectName = 'SHAPE';  //??? can't add Part.reshapeObjectName property
  // there's no Part.reshapeAdornmentTemplate either

  // internal state
  /** @type {GraphObject} */
	this._handle = null;
  /** @type {Shape} */
	this._adornedShape = null;
  /** @type {Geometry} */
	this._originalGeometry = null;  // in case the tool is cancelled and the UndoManager is not enabled
	
}
go.Diagram.inherit(GeometryReshapingTool, go.Tool);


/*
* A small GraphObject used as a reshape handle for each segment.
* The default GraphObject is a small blue diamond.
* @name GeometryReshapingTool#handleArchetype 
* @function.
* @return {GraphObject}
*/
Object.defineProperty(GeometryReshapingTool.prototype, "handleArchetype", {
  get: function() { return this._handleArchetype; },
  set: function(val) { this._handleArchetype = value; }
});

/*
* The name of the GraphObject to be reshaped.
* @name GeometryReshapingTool#reshapeObjectName
* @function.
* @return {string}
*/
Object.defineProperty(GeometryReshapingTool.prototype, "reshapeObjectName", {
  get: function() { return this._reshapeObjectName; },
  set: function(val) { this._reshapeObjectName = value; }
});

/*
* This read-only property returns the {@link GraphObject} that is the tool handle being dragged by the user.
* This will be contained by an {@link Adornment} whose category is "GeometryReshaping".
* Its {@link Adornment#adornedObject} is the same as the {@link #adornedShape}.
* @name GeometryReshapingTool#handle
* @function.
* @return {GraphObject}
*/
Object.defineProperty(GeometryReshapingTool.prototype, "handle", {
  get: function() { return this._handle; }
});

/*
* Gets the {@link Shape} that is being reshaped.
* This must be contained within the selected Part.
* @name GeometryReshapingTool#adornedShape
* @function.
* @return {Shape}
*/
Object.defineProperty(GeometryReshapingTool.prototype, "adornedShape", {
  get: function() { return this._adornedShape; }
});

/*
* This read-only property remembers the original value for {@link Shape#geometry},
* so that it can be restored if this tool is cancelled.
* @name GeometryReshapingTool#originalGeometry
* @function.
* @return {Geometry}
*/
Object.defineProperty(GeometryReshapingTool.prototype, "originalGeometry", {
  get: function() { return this._originalGeometry; }
});


/**
* Show an {@link Adornment} with a reshape handle at each point of the geometry.
* Don't show anything if {@link #reshapeObjectName} doesn't identify a {@link Shape}
* that has a {@link Shape#geometry} of type {@link Geometry#Path}.
* @this {GeometryReshapingTool}
* @param {Part} part the part.
*/
GeometryReshapingTool.prototype.updateAdornments = function(part) {
  if (part === null || part instanceof go.Link) return;  // this tool never applies to Links
  if (part.isSelected && !this.diagram.isReadOnly) {
    part.name = 'GeometryReshaping';
    
    
    var selelt = part.findObject(this.reshapeObjectName);
    if (selelt instanceof go.Shape && selelt.actualBounds.isReal() && selelt.isVisibleObject() &&
        part.canReshape() && part.actualBounds.isReal() && part.isVisible() &&
        selelt.geometry.type === go.Geometry.Path) {
      var adornment = part.findAdornment(this.name);
      if (adornment === null) {
        adornment = this.makeAdornment(selelt);
      }
      if (adornment !== null) {
        // update the position/alignment of each handle
        var geo = selelt.geometry;
        var b = geo.bounds;
        // update the size of the adornment
        adornment.findObject("BODY").desiredSize = b.size;
        adornment.elements.each(function(h) {
          if (h._typ === undefined) return;
          var fig = geo.figures.elt(h._fig);
          var seg = fig.segments.elt(h._seg);
          var x = 0;
          var y = 0;
          switch (h._typ) {
            case 0: x = fig.startX; y = fig.startY; break;
            case 1: x = seg.endX; y = seg.endY; break;
            case 2: x = seg.point1X; y = seg.point1Y; break;
            case 3: x = seg.point2X; y = seg.point2Y; break;
          }
          var bw = b.width;
          if (bw === 0) bw = 0.001;
          var bh = b.height;
          if (bh === 0) bh = 0.001;
          h.alignment = new go.Spot(Math.max(0, Math.min((x - b.x) / bw, 1)),
                                    Math.max(0, Math.min((y - b.y) / bh, 1)));
        });
        adornment.name = 'GeometryReshapingAdornment';
        adornment.layerName = 'LayerBeforeForeground';

        part.addAdornment(this.name, adornment);
        adornment.location = selelt.getDocumentPoint(go.Spot.TopLeft);
        adornment.angle = selelt.getDocumentAngle();
        return;
      }
    }
  }
  part.removeAdornment(this.name);
};

/*
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.makeAdornment = function(selelt) {
  var adornment = new go.Adornment();
  adornment.type = go.Panel.Spot;
  adornment.locationObjectName = "BODY";
  adornment.locationSpot = new go.Spot(0, 0, -selelt.strokeWidth / 2, -selelt.strokeWidth / 2);
  var h = new go.Shape();
  h.name = "BODY";
  h.fill = null;
  h.stroke = null;
  h.strokeWidth = 0;
  adornment.add(h);

  var geo = selelt.geometry;
  // requires Path Geometry, checked above in updateAdornments
  for (var f = 0; f < geo.figures.count; f++) {
    var fig = geo.figures.elt(f);
    for (var g = 0; g < fig.segments.count; g++) {
      var seg = fig.segments.elt(g);
      var h;
      if (g === 0) {
        h = this.makeHandle(selelt, fig, seg);
        if (h !== null) {
          h._typ = 0;
          h._fig = f;
          h._seg = g;
          adornment.add(h);
        }
      }
      h = this.makeHandle(selelt, fig, seg);
      if (h !== null) {
        h._typ = 1;
        h._fig = f;
        h._seg = g;
        adornment.add(h);
      }
      if (seg.type === go.PathSegment.QuadraticBezier || seg.type === go.PathSegment.Bezier) {
        h = this.makeHandle(selelt, fig, seg);
        if (h !== null) {
          h._typ = 2;
          h._fig = f;
          h._seg = g;
          adornment.add(h);
        }
        if (seg.type === go.PathSegment.Bezier) {
          h = this.makeHandle(selelt, fig, seg);
          if (h !== null) {
            h._typ = 3;
            h._fig = f;
            h._seg = g;
            adornment.add(h);
          }
        }
      }
    }
  }
  adornment.category = this.name;
  adornment.adornedObject = selelt;
  return adornment;
};

/*
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.makeHandle = function(selelt, fig, seg) {
  var h = this.handleArchetype;
  if (h === null) return null;
  return h.copy();
};


/*
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.canStart = function() {
  if (!this.isEnabled) return false;

  var diagram = this.diagram;
  if (diagram === null || diagram.isReadOnly) return false;
  if (!diagram.allowReshape) return false;
  if (!diagram.lastInput.left) return false;
  var h = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
  return (h !== null);
};

/**
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.doActivate = function() {
  var diagram = this.diagram;
  if (diagram === null) return;
  this._handle = this.findToolHandleAt(diagram.firstInput.documentPoint, this.name);
  if (this._handle === null) return;
  var shape = this._handle.part.adornedObject;
  if (!shape) return;
  this._adornedShape = shape;
  diagram.isMouseCaptured = true;
  this.startTransaction(this.name);
  this._originalGeometry = shape.geometry;
  this.isActive = true;
};

/**
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.doDeactivate = function() {
  this.stopTransaction();

  this._handle = null;
  this._adornedShape = null;
  var diagram = this.diagram;
  if (diagram !== null) diagram.isMouseCaptured = false;
  this.isActive = false;
};

/**
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.doCancel = function() {
  var shape = this._adornedShape;
  if (shape !== null) {
    // explicitly restore the original route, in case !UndoManager.isEnabled
    shape.geometry = this._originalGeometry;
  }
  this.stopTool();
};

/**
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.doMouseMove = function() {
  var diagram = this.diagram;
  if (this.isActive && diagram !== null) {
    var newpt = this.computeReshape(diagram.lastInput.documentPoint);
    this.reshape(newpt);
  }
};

/**
* @this {GeometryReshapingTool}
*/
GeometryReshapingTool.prototype.doMouseUp = function() {
  var diagram = this.diagram;
  if (this.isActive && diagram !== null) {
    var newpt = this.computeReshape(diagram.lastInput.documentPoint);
    this.reshape(newpt);
    this.transactionResult = this.name;  // success
  }
  this.stopTool();
};

/**
* @expose
* @this {GeometryReshapingTool}
* @param {Point} newPoint the value of the call to {@link #computeReshape}.
*/
GeometryReshapingTool.prototype.reshape = function(newPoint) {
  var shape = this.adornedShape;
  var locpt = shape.getLocalPoint(newPoint);
  var geo = shape.geometry.copy();
  shape.desiredSize = new go.Size(NaN, NaN); // set the desiredSize once we've gotten our Geometry so we don't clobber
  var type = this.handle._typ;
  if (type === undefined) return;
  var fig = geo.figures.elt(this.handle._fig);
  var seg = fig.segments.elt(this.handle._seg);
  switch (type) {
    case 0: fig.startX = locpt.x; fig.startY = locpt.y; break;
    case 1: seg.endX = locpt.x; seg.endY = locpt.y; break;
    case 2: seg.point1X = locpt.x; seg.point1Y = locpt.y; break;
    case 3: seg.point2X = locpt.x; seg.point2Y = locpt.y; break;
  }
  var offset = geo.normalize();  // avoid any negative coordinates in the geometry
  shape.geometry = geo;  // modify the Shape
  var part = shape.part;  // move the Part holding the Shape
  if (!part.locationSpot.equals(go.Spot.Center)) {  // but only if the locationSpot isn't Center
    part.move(part.position.copy().subtract(offset));
  }
  this.updateAdornments(part);  // update any Adornments of the Part
  this.diagram.maybeUpdate();  // force more frequent drawing for smoother looking behavior
};


/**
* @expose
* @this {GeometryReshapingTool}
* @param {Point} p the point where the handle is being dragged.
* @return {Point}
*/
GeometryReshapingTool.prototype.computeReshape = function(p) {
  return p;  // no constraints on the points
};