(function(){
  angular.module('svgApp', []);
// creating the main module for the app
})()
<!DOCTYPE html>
<html ng-app="svgApp">

<head>
	<meta charset="utf-8" />
	<title>AngularJS Plunker</title>
	<script>
		document.write('<base href="' + document.location + '" />');
	</script>
	<link rel="stylesheet" href="style.css" />
	<script data-require="angular.js@1.5.x" src="https://code.angularjs.org/1.5.8/angular.js" data-semver="1.5.8">
	</script>
	<script src="http://d3js.org/d3.v3.min.js"></script>
  
</head>

<body>
	<svg svg-directive="" ng-controller="SvgCtrl as vm" width="100%" height="100%" viewBox="0 0 1000 1000" style="border:1px solid black"
	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

		<g class="elem-group">
			<image xlink:href="" class="image-element" preserveAspectRatio="none" ng-attr-transform="{{vm.model.image.transform.translate}}{{vm.model.image.transform.rotate}}"
			 ng-attr-xlink:href="{{vm.model.image.url}}" ng-attr-width="{{vm.model.image.width}}" ng-attr-height="{{vm.model.image.height}}"
			 ng-attr-x="{{vm.model.image.x}}" ng-attr-y="{{vm.model.image.y}}"></image>
		</g>

		<g class="controls-group" ng-attr-transform="{{vm.model.image.transform.translate}}{{vm.model.image.transform.rotate}}">
			<!-- the main rect that is over the element -->
			<rect class="move-rect" fill-opacity="0" stroke="#64686f" stroke-width="2" ng-attr-x="{{vm.model.image.x}}" ng-attr-y="{{vm.model.image.y}}"
			 ng-attr-width="{{vm.model.image.width}}" ng-attr-height="{{vm.model.image.height}}"></rect>

			<!-- the resize handles -->
			<rect class="resize-tl" ng-attr-x="{{vm.model.image.x-10}}" ng-attr-y="{{vm.model.image.y-10}}" fill="white" stroke="#64686f"
			 stroke-width="2" width="20" height="20"></rect>
			<rect class="resize-tr" ng-attr-x="{{vm.model.image.x+vm.model.image.width-10}}" ng-attr-y="{{vm.model.image.y-10}}" fill="red"
			 stroke="#64686f" stroke-width="2" width="20" height="20"></rect>
			<rect class="resize-bl" ng-attr-x="{{vm.model.image.x-10}}" ng-attr-y="{{vm.model.image.y+vm.model.image.height-10}}" fill="blue"
			 stroke="#64686f" stroke-width="2" width="20" height="20"></rect>
			<rect class="resize-br" ng-attr-x="{{vm.model.image.x+vm.model.image.width-10}}" ng-attr-y="{{vm.model.image.y+vm.model.image.height-10}}"
			 fill="yellow" stroke="#64686f" stroke-width="2" width="20" height="20"></rect>

			<!-- the rotate handles -->
			<circle class="rotate-tl" fill="white" stroke="#64686f" stroke-width="2" ng-attr-cx="{{vm.model.image.x-20}}" ng-attr-cy="{{vm.model.image.y-20}}"
			 r="12"></circle>
			<circle class="rotate-tr" fill="red" stroke="#64686f" stroke-width="2" ng-attr-cx="{{(vm.model.image.width+vm.model.image.x)+20}}"
			 ng-attr-cy="{{vm.model.image.y-20}}" r="12"></circle>
			<circle class="rotate-bl" fill="blue" stroke="#64686f" stroke-width="2" ng-attr-cx="{{vm.model.image.x-20}}" ng-attr-cy="{{(vm.model.image.height+vm.model.image.y)+20}}"
			 r="12"></circle>
			<circle class="rotate-br" fill="yellow" stroke="#64686f" stroke-width="2" ng-attr-cx="{{(vm.model.image.width+vm.model.image.x)+20}}"
			 ng-attr-cy="{{(vm.model.image.height+vm.model.image.y)+20}}" r="12"></circle>

		</g>

		<!-- this circle is not in the center of the element after the angle !== 0 -->
		<g class="g-red-center-circle">
			<circle class="red-center-circle" fill="red" stroke-width="1" stroke="white" r="5" ng-attr-cx="{{vm.model.image.rotate.cx}}"
			 ng-attr-cy="{{vm.model.image.rotate.cy}}" ng-attr-transform="{{vm.model.image.transform.translate}}"></circle>
		</g>
		<!-- this circle is in the correct center, even after the angle !== -->
		<g class="g-blue-center-circle">
			<circle class="blue-center-circle" fill="blue" stroke-width="1" stroke="white" r="5" ng-attr-cx="{{vm.model.image.rotate.cx}}"
			 ng-attr-cy="{{vm.model.image.rotate.cy}}" ng-attr-transform="{{vm.model.image.transform.translate}}{{vm.model.image.transform.rotate}}"></circle>
		</g>

	</svg>
  <script src="svgApp.module.js"></script>
	<script src="svgApp.config.js"></script>
	<script src="svgApp.service.js"></script>
	<script src="svgApp.controller.js"></script>
	<script src="svgApp.directive.js"></script>
</body>

</html>
/* Put your css in here */

(function(){
  function SvgService(){
  /**
   * Service model object
   * */
  var model = {
    image : {
      url : "http://i.imgur.com/hSqdOZI.jpg",
      width : 680, 
      height : 510, 
      x : 50, y : 50,
      rotate : {
        angle : 0, cx : 0, cy : 0
      },
      translate : {
        x : 0, y : 0
      },
      transform : {
        rotate : null, translate : null
      }
    }
  };

  // exports public functions and variables
  return {
    // variables
    model : model,
    // functions
    updateImageTransform : updateImageTransform,
    updateImageCoordinates : updateImageCoordinates,
    getImageUpdatedCoordinates : getImageUpdatedCoordinates,
    updateImageRotateCoordinates : updateImageRotateCoordinates,
    updateImageTranslateCoordinates : updateImageTranslateCoordinates,
    getImageUpdatedRotateCoordinates : getImageUpdatedRotateCoordinates,
    getImageUpdatedTranslateCoordinates : getImageUpdatedTranslateCoordinates
  };
  
  /////////////////////////////////////////////////////////////
  
  function updateImageTranslateCoordinates(x, y){
    model.image.translate.x = x || model.image.translate.x;
    model.image.translate.y = y || model.image.translate.y;
  }
  
  function getImageUpdatedTranslateCoordinates(){
    return model.image.translate;
  }
  
  /**
   * Function to get the current image rotate coordinate values
   * */
  function getImageUpdatedRotateCoordinates(){
    return model.image.rotate;
  }
  
  /**
   * Function to get the current image coordinate values 
   */
  function getImageUpdatedCoordinates(){
    return {
      width : model.image.width, 
      height : model.image.height,
      x : model.image.x, 
      y : model.image.y
    };
    
  }
  
  /**
   * Function to update the rotate values
   * */
  function updateImageRotateCoordinates(angle, cx, cy){
    angle = angle || model.image.rotate.angle;
    cx = cx || model.image.rotate.cx;
    cy = cy || model.image.rotate.cy;
    
    model.image.rotate.angle = angle;
    model.image.rotate.cx = cx;
    model.image.rotate.cy = cy;
    
  }
  
  /**
   * Function to update the transform string value 
   * for the passed transform attr
   * */
  function updateImageTransform(transform, stringValue){
    model.image.transform[transform] = stringValue;
  }
  
  /**
   * Function to update the image position and dimension 
   * coordinate values
   * */
  function updateImageCoordinates(width, height, x, y){
    
    width = width || model.image.width;
    height = height || model.image.height;
    x = x || model.image.x;
    y = y || model.image.y;
    
    model.image.width = width;
    model.image.height = height;
    model.image.x = x;
    model.image.y = y;

  }
}

angular
  .module('svgApp')
  .factory('SvgService', SvgService);
})()
(function() {
  SvgCtrl.$inject = ['SvgService'];
  function SvgCtrl(SvgService) {
    var model = SvgService.model;

    angular.extend(this, {
      model: model,
    });
  }

  angular.module('svgApp').controller('SvgCtrl', SvgCtrl);
})();
(function() {
  SvgAppConfig.$inject = ['$sceDelegateProvider'];
  function SvgAppConfig($sceDelegateProvider) {
    // needed to load external images in svg
    $sceDelegateProvider.resourceUrlWhitelist(['**']);
  }

  angular.module('svgApp').config(SvgAppConfig);
})();
(function() {
  SvgDirective.$inject = ['SvgService'];
  function SvgDirective(SvgService) {
    function SvgDirectiveLink(scope, elem, attrs) {
      // selecting svg elements via d3js
      var mainSvg = d3.select(elem);
      var controlsGroup = d3.select('g.controls-group');
      var topLeftRotateHandleElem = controlsGroup.select('circle.rotate-tl');

      var blueCenterCircle = d3.select('circle.blue-center-circle');

      // when the directive is binded, stores the original
      // coordinates of the image to be used as validation
      // by the resize function
      var originalCoordinates = SvgService.getImageUpdatedCoordinates();
      // sets the minimun values for width and height to be of 10% of the original
      // dimensions, to avoid negative values
      var minWidth = 0.1 * originalCoordinates.width;
      var minHeight = 0.1 * originalCoordinates.height;

      // transform position, used to move the object around with the drag and drop
      var tPos = SvgService.getImageUpdatedTranslateCoordinates();

      var elemCenter = getElementCenter();
      SvgService.updateImageRotateCoordinates(null, elemCenter.x, elemCenter.y);

      // do the bind of the drag behavior in the controls elements
      controlsGroup.call(bindControlsDragAndDrop());

      /**
       * Function used to bind the drag and drop behavior of the controls
       */

      function bindControlsDragAndDrop() {
        // auxiliar variables
        var target, targetClass, rotateHandleStartPos;

        // binding the behavior callback functions
        var drag = d3.behavior
          .drag()
          .on('dragstart', dragStart)
          .on('drag', dragMove)
          .on('dragend', dragEnd);

        return drag;

        /**
         * For the drag action starts
         * */
        function dragStart() {
          // gets the current target element where the drag event started
          target = d3.select(d3.event.sourceEvent.target);
          // and saves the target element class in a aux variable
          targetClass = target.attr('class');

          // when the user is rotating the element, stores the initial angle
          // information to be used in the rotate function
          if (targetClass.indexOf('rotate') > -1) {
            // gets the updated rotate coordinates
            var updatedRotateCoordinates = SvgService.getImageUpdatedRotateCoordinates();

            // updates the rotate handle start posistion object with
            // basic information from the model and the handles
            rotateHandleStartPos = {
              angle: updatedRotateCoordinates.angle, // the current angle
              x: parseFloat(target.attr('cx')), // the current cx value of the target handle
              y: parseFloat(target.attr('cy')), // the current cy value of the target handle
            };

            // calc the rotated top & left corner
            if (rotateHandleStartPos.angle > 0) {
              var correctsRotateHandleStartPos = getHandleRotatePosition(
                rotateHandleStartPos
              );
              rotateHandleStartPos.x = correctsRotateHandleStartPos.x;
              rotateHandleStartPos.y = correctsRotateHandleStartPos.y;
            }

            // adds the initial angle in degrees
            rotateHandleStartPos.iniAngle = calcAngleDeg(
              updatedRotateCoordinates,
              rotateHandleStartPos
            );
          }
        }

        /**
         * For while the drag is happening
         * */
        function dragMove() {
          // checks the target class to choose the right function
          // to be executed while dragging
          // #1 - If the user is moving the element around
          if (targetClass.indexOf('move') > -1) {
            moveObject();
          }
          // #2 - If the user is resizing the element
          else if (targetClass.indexOf('resize') > -1) {
            resizeObject(targetClass);
          }
          // #3 - If the user is rotating the element
          else if (targetClass.indexOf('rotate') > -1) {
            rotateObject(rotateHandleStartPos);
          }

          // apply the scope changes for any function that might
          // have been called, to keep things updated in the service model
          scope.$apply();
        }

        /**
         * For when the drag stops (the user release the element)
         * */
        function dragEnd() {
          // check if the user was resizing
          if (targetClass.indexOf('resize') > -1) {
            // updates the center of rotation after resizing the element
            elemCenter = getElementCenter();
            SvgService.updateImageRotateCoordinates(
              null,
              elemCenter.x,
              elemCenter.y
            );
            scope.$apply();
          }
        }
      }

      /**
       * Function to move the object around
       * */
      function moveObject() {
        // increments the x/y values with the dx/dy values from the d3.event object
        tPos.x += d3.event.dx;
        tPos.y += d3.event.dy;

        // updates the translate transform values to move the object to the new point
        SvgService.updateImageTransform(
          'translate',
          'translate(' + [tPos.x, tPos.y] + ')'
        );
        // updates the translate coordinates in the model for further use
        SvgService.updateImageTranslateCoordinates(tPos.x, tPos.y);
      }

      /**
       * Function to resize the object based in the passed direction
       * */
      function resizeObject(direction) {
        // gets the original image coordinates from the service model
        var updatedCoordinates = SvgService.getImageUpdatedCoordinates();

        // auxiliar variables
        var x, y, width, height;

        // do the resize math based in the direction that the resize started
        switch (direction) {
          // top left
          case 'resize-tl':
            width = updatedCoordinates.width - d3.event.dx;
            height = updatedCoordinates.height - d3.event.dy;
            x = updatedCoordinates.x + (updatedCoordinates.width - width);
            y = updatedCoordinates.y + (updatedCoordinates.height - height);
            break;
          // top right
          case 'resize-tr':
            width = updatedCoordinates.width + d3.event.dx;
            height = updatedCoordinates.height - d3.event.dy;
            y = updatedCoordinates.y + (updatedCoordinates.height - height);
            break;
          // bottom left
          case 'resize-bl':
            width = updatedCoordinates.width - d3.event.dx;
            height = updatedCoordinates.height + d3.event.dy;
            x = updatedCoordinates.x + (updatedCoordinates.width - width);
            break;
          // bottom right
          case 'resize-br':
            width = updatedCoordinates.width + d3.event.dx;
            height = updatedCoordinates.height + d3.event.dy;
            break;
        }

        if (width > minWidth && height > minHeight) {
          // updates the image object model with the new coordinates values
          SvgService.updateImageCoordinates(width, height, x, y);
        }
      }

      /**
       * Function to rotate the object based in the initial rotation values
       * present in the rotateHandleStartPos object
       * */
      function rotateObject(rotateHandleStartPos) {
        // gets the current udapted rotate coordinates
        var updatedRotateCoordinates = SvgService.getImageUpdatedRotateCoordinates();

        // increments the mouse event starting point with the mouse movement event
        rotateHandleStartPos.x += d3.event.dx;
        rotateHandleStartPos.y += d3.event.dy;

        // calculates the difference between the current mouse position and the center line
        var angleFinal = calcAngleDeg(
          updatedRotateCoordinates,
          rotateHandleStartPos
        );
        // gets the difference of the angles to get to the final angle
        var angle =
          rotateHandleStartPos.angle +
          angleFinal -
          rotateHandleStartPos.iniAngle;

        // converts the values to stay inside the 360 positive
        angle %= 360;
        if (angle < 0) {
          angle += 360;
        }

        // creates the new rotate position array
        var rotatePos = [
          angle,
          updatedRotateCoordinates.cx,
          updatedRotateCoordinates.cy,
        ];

        // updates the transform rotate string value and the rotate info in the service model
        SvgService.updateImageTransform('rotate', 'rotate(' + rotatePos + ')');
        // and updates the current angle with the new one
        SvgService.updateImageRotateCoordinates(angle);
      }

      //////////////

      /**
       * Private functions
       * */
      // gets the passed d3 element center coordinates
      function getElementCenter() {
        var uCords = SvgService.getImageUpdatedCoordinates();
        var result = {
          x: uCords.x + uCords.width / 2,
          y: uCords.y + uCords.height / 2,
        };
        return result;
      }

      /**
       * Function to corrects the rotate handles starting position
       */
      function getHandleRotatePosition(handleStartPos) {
        // its possible to use "cx/cy" for properties
        var originalX = handleStartPos.x ? handleStartPos.x : handleStartPos.cx;
        var originalY = handleStartPos.y ? handleStartPos.y : handleStartPos.cy;

        // gets the updated element center, without rotatio
        var center = getElementCenter();
        // calculates the rotated handle position considering the current center as
        // pivot for rotation
        var dx = originalX - center.x;
        var dy = originalY - center.y;
        var theta = (handleStartPos.angle * Math.PI) / 180;

        return {
          x: dx * Math.cos(theta) - dy * Math.sin(theta) + center.x,
          y: dx * Math.sin(theta) + dy * Math.cos(theta) + center.y,
        };
      }

      // gets the angle in degrees between two points
      function calcAngleDeg(p1, p2) {
        var p1x = p1.x ? p1.x : p1.cx;
        var p1y = p1.y ? p1.y : p1.cy;
        return (Math.atan2(p2.y - p1y, p2.x - p1x) * 180) / Math.PI;
      }
    }

    return {
      strict: 'A',
      link: SvgDirectiveLink,
    };
  }

  angular.module('svgApp').directive('svgDirective', SvgDirective);
})();