Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/476.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Paper.js背景光栅化故障_Javascript_Image_Canvas_Raster_Paperjs - Fatal编程技术网

Javascript Paper.js背景光栅化故障

Javascript Paper.js背景光栅化故障,javascript,image,canvas,raster,paperjs,Javascript,Image,Canvas,Raster,Paperjs,我们正在使用Paper.js构建各种图像编辑器。我们在Paper.js画布的一侧有一个队列,允许在图像之间切换。每次在图像之间切换时,我们都希望将所有注释(光栅化)展平到刚刚编辑的图像上 每次切换图像时,都会调用此方法,将当前图像和注释光栅化为数据URL。(如果我们重新访问此图像,将显示此数据URL中的光栅。) 然后我们调用这个方法,它改变了我们正在编辑的图像: var setCanvasImage = function(imageObject) { if(imageObject != nu

我们正在使用Paper.js构建各种图像编辑器。我们在Paper.js画布的一侧有一个队列,允许在图像之间切换。每次在图像之间切换时,我们都希望将所有注释(光栅化)展平到刚刚编辑的图像上

每次切换图像时,都会调用此方法,将当前图像和注释光栅化为数据URL。(如果我们重新访问此图像,将显示此数据URL中的光栅。)

然后我们调用这个方法,它改变了我们正在编辑的图像:

var setCanvasImage = function(imageObject) {
  if(imageObject != null)
  {
    imageHeight = imageObject.height;
    var imageWidth = imageObject.width;

    // Set up HTMLImage
    var imageElement = new Image(imageObject.width, imageObject.height);
    if(_.has(imageObject, 'imageData')) { // Came as 64 bit data
      imageElement.src = 'data:image/png;base64,' + imageObject.imageData;
    } else if(_.has(imageObject, 'imageUrl')) { // Came as URL
      imageElement.src = imageObject.imageUrl;
    }

    // Add image to Paper.js canvas
    imageElement.onload = function(event) {

      // Initialize Paper.js on the canvas
      paper.setup(canvas);

      raster = new paper.Raster(imageElement, new paper.Point(canvas.width / 2, canvas.height / 2));

      setUpNotes();

      selectedItems = new paper.Group(); // Since Paper.js has been setup we can now initialize this Group
      registerCanvasEvents(); // Panning, zooming, moving selected items, deselecting all selected items

      fitToPage();
    };
  }
};
因此,这会改变图像,但当我在队列中选择不同的图像后将鼠标移到画布中时,它会与我们刚刚打开的图像(及其注释)发生故障,直到我执行平移、缩放等操作。然后我看到我选择的图像并真正使用它

删除
plantToDataUrl()
功能可使队列无缝工作。所以在我看来,那里有些可疑之处。我们正在使用该方法生成Paper.js光栅对象。光栅似乎会自动添加自己。我试图通过打电话给

layerAsRaster.visible = false;
但是没有用

是什么导致了这种故障行为?如何预防

更新

为了清晰(希望如此)和完整,我决定发布我们与React一起使用的整个PaperFunctions类,React承载了
元素。有很多代码和大量清理工作要做,特别是在
registerCanvasEvents()
中。请容忍这个初学者。而且它有几百行,所以将它粘贴到您最喜欢的编辑器中可能会有所帮助。入口点包括
setCanvas
,它在带有
元素的React类的
componentDidMount
中调用,以及从队列中调用的
canvasSetImage
。我同意Bmacnouton的回答,每次加载新图像时调用
paper.setup(canvas)
都很奇怪。我目前正在研究解决这个问题的正确方法,把它放在正确的地方
setCanvas
似乎合乎逻辑,但当我在该设置中拖动图像以移动它时,它会留下一条图像轨迹。总之,这里是PaperFunctions.js:

var JQueryMousewheel = require('jquery-mousewheel')($);

var SimplePanAndZoom = require('./SimplePanAndZoom.js');
var panAndZoom = new SimplePanAndZoom();

var selectedItems;

// We use selection here in two distinct ways.
// An item may be Paper.js selected but not in the selection group.
// This is because we want to show it is selectable.
// A blue bounding box indicates it is selectable.
// A green bounding box indicates it has actually been selected and added to selectedItems.
// Only things in selectedItems are actually operated on.
// So the event handlers in this method basically set up whether or not the item is in selectedItems (and therefore will be operated on for moving, resizing, deleting, etc.).
// That is, the event handlers here are concerned with displaying to the user the status of selection for the item - whether or not it will be operated on when events actually happen on the selectedItems Group.
var registerItemEvents = function(item) {
  // Boolean flag for mouseup to know if was drag or click
  var dragged;

  // For tracking if dragging or clicking is happening
  item.on('mousedown', function(e) {
    dragged = false;
  });

  // On click toggle selection
  item.on('mouseup', function(event) {
    event.stopPropagation(); // Only for item applied to
    event.preventDefault();

    if(!dragged) {
      var justAdded = addIfNotInSelectedItems(item);
      if(!justAdded) { // Item was in selection group, remove it
        item.remove();
        paper.project.activeLayer.addChild(item);

        this.selectedColor = paper.project.activeLayer.selectedColor;
        //item.selected = false;
      }
    }
  });

  // Show as selectable even after has been deselected
  item.on('mousemove', function(event) {
    this.selected = true;
  })

  // If not selected, on mouse enter show that it is selectable
  item.on('mouseenter', function(event) {
    if(!this.selected) {
      this.selected = true;
    }
  });

  // If not selected, on mouse leave remove indicator that is selectable
  item.on('mouseleave', function(event) {
    var isInSelectedItems = selectedItems.getItem(item);
    if(this.selected && isInSelectedItems == null) {
      this.selected = false;
    }
  });

  // On drag, move item
  item.on('mousedrag', function(event) {
    dragged = true;

    // If user starts dragging automatically select the item
    addIfNotInSelectedItems(item);
  });
}

var addIfNotInSelectedItems = function(item) {
  var isInSelectedItems = selectedItems.getItem(item);
  if(isInSelectedItems == null) { // Item not currently in selection group, add it
    selectedItems.addChild(item);
    item.selectedColor = 'green';
    item.selected = true;
    return true; // Was added, return true
  } else {
    return false; // Already in, return false
  }
}

var registerCanvasEvents = function() {
  if(paper.view != null && canvas != null) {
    // Zoom on mousewheel
    $(canvas).mousewheel(function(event) {
      event.preventDefault();
      var mousePosition = new paper.Point(event.offsetX, event.offsetY);
      var viewPosition = paper.view.viewToProject(mousePosition);
      var returnedValues = panAndZoom.changeZoom(paper.view.zoom, (event.deltaY * -1), paper.view.center, viewPosition, 1.1);
      var newZoom = returnedValues[0];
      var offset = returnedValues[1];
      paper.view.zoom = newZoom;
      paper.view.center = paper.view.center.add(offset);
    });

    // For tracking if dragging or clicking is happening
    var dragged;
    paper.project.layers[0].on('mousedown', function(e) { // TODO should be layer 0 in long run?
      dragged = false;
    });


    // Pan on mouse drag
    /*paper.project.layers[0].on('mousedrag', function(event) { // TODO should be layer 0 in long run?
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }
    });*/

    // Move selected items on mouse drag
    selectedItems.on('mousedrag', function(event) {
      event.stopPropagation(); // Don't propogate up or it will register as a pan event
      event.preventDefault();

      dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click

      this.translate(new paper.Point(event.delta.x, event.delta.y));
    });

    // If was a click and not a drag, deselect selected items
    paper.project.layers[0].on('mouseup', function(event) {
      if(!dragged) {
        var removedItems = selectedItems.removeChildren(); // Remove from selection group, which also removes from display
        paper.project.activeLayer.addChildren(removedItems); // Return to display

        // Reset selection colors for showing selectable
        for(var i =0; i < removedItems.length; i++) {
          removedItems[i].selectedColor = paper.project.activeLayer.selectedColor;
          removedItems[i].selected = false;
        }
      }
    });

    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    var tool = new paper.Tool();

    var startDragPoint;

    // Capture start of drag selection
    paper.tool.onMouseDown = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        startDragPoint = new paper.Point(event.point);
      }
    };

    paper.tool.onMouseDrag = function(event) {
      // Panning
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }

      // Show box indicating the area that has been selected
      // For moving area and whiting out area
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        dragged = true;
        var showSelection = new paper.Path.Rectangle({
            from: startDragPoint,
            to: event.point,
            strokeColor: 'red',
            strokeWidth: 1
        });

        // Stop showing the selected area on drag (new one is created) and up because we're done
        showSelection.removeOn({
            drag: true,
            up: true
        });
      }
    };

    // Capture start of drag selection
    paper.tool.onMouseUp = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        var endDragPoint = new paper.Point(event.point);
        if(event.event.ctrlKey && event.event.shiftKey) { // Whiteout area
          whiteoutArea(startDragPoint, endDragPoint);
        } else if(event.event.ctrlKey && event.event.altKey) { // Move selected area
          selectArea(startDragPoint, endDragPoint);
        }
      }
    };

    // Key events
    paper.tool.onKeyUp = function(event) {
      // Delete selected items on delete key
      if(event.key == 'delete') {
        selectedItems.removeChildren();
      } else if (event.key == 'option') {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    }
  }
}

// These variables are scoped so that all methods in PaperFunctions can access them
var canvas; // Set by setCanvas
var imageHeight; // Set by setCanvasImage

var raster;

var toolsSetup = false;

var setCanvas = function(canvasElement) {
  canvas = canvasElement;
  paper.setup(canvas);
};

var setCanvasImage = function(imageObject) {
  if(imageObject != null)
  {
    imageHeight = imageObject.height;
    var imageWidth = imageObject.width;

    // Set up HTMLImage
    var imageElement = new Image(imageObject.width, imageObject.height);
    if(_.has(imageObject, 'imageData')) { // Came as 64 bit data
      imageElement.src = 'data:image/png;base64,' + imageObject.imageData;
    } else if(_.has(imageObject, 'imageUrl')) { // Came as URL
      imageElement.src = imageObject.imageUrl;
    }

    // Add image to Paper.js canvas
    imageElement.onload = function(event) {
      //canvas.height = $(document).height()-3; // Set canvas height. Why do this here and not in the React component? Because we set the width here too, so we're keeping those together. Perhaps in the future this will be changed when we are responsive to window resizing.
      //scalingFactor = canvas.height / imageObject.height; // Determine the ratio
      //canvas.width = imageElement.width * scalingFactor; // Scale width based on height; canvas height has been set to the height of the document

      // Initialize Paper.js on the canvas
      paper.setup(canvas);

      raster = new paper.Raster(imageElement, new paper.Point(canvas.width / 2, canvas.height / 2));

      //setUpLineAndFreeFormDrawing(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      setUpNotes(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      selectedItems = new paper.Group(); // Since Paper.js has been setup we can now initialize this Group
      registerCanvasEvents(); // Panning, zooming, moving selected items, deselecting all selected items

      fitToPage();
    };
  }
};

var fitToPage = function() {
  if(paper.view != null && canvas != null) {
    // Fit image to page so whole thing is displayed
    var scalingFactor = canvas.height / imageHeight; // Constant representation of the ratio of the canvas size to the image size
    var zoomFactor = scalingFactor / paper.view.zoom; // Dynamic representation of the zoom needed to return to viewing the whole image in the canvas

    // Reset the center point to the center of the canvas
    var canvasCenter = new paper.Point(canvas.width/2, canvas.height/2);
    paper.view.center = canvasCenter;

    // Zoom to fit the whole image in the canvas
    var returnedValues = panAndZoom.changeZoom(paper.view.zoom, -1, canvasCenter, canvasCenter, zoomFactor); // Always pass -1 as the delta, not entirely sure why
    var newZoom = returnedValues[0];
    var offset = returnedValues[1];
    paper.view.zoom = newZoom;
    paper.view.center = paper.view.center.add(offset);
  }
};

var addImage = function(imageDataUrl) {
  if(paper.view != null) {
    var img = document.createElement("img");
    img.src = imageDataUrl;
    var presentMomentForId = new Date().getTime() + "-image"; // For purposes of having unique IDs
    img.id = presentMomentForId;
    img.hidden = true;
    document.body.appendChild(img);

    var raster = new paper.Raster(presentMomentForId);

    registerItemEvents(raster);
  }
};

var setUpLineAndFreeFormDrawing = function() {
  if(paper.project != null) {
    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    // Each time Alt comes up, start a new path
    paper.tool.onKeyUp = function(event) {
      if(event.key == "option") {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    };
  }
};

// Establishes default line style
var newPath = function() {
  var path = new paper.Path();
  path.strokeColor = 'black';
  path.strokeWidth = 10;
  return path;
};

var note = "";
var setNote = function(newNote) {
  note = newNote;
};

var setUpNotes = function() {
  if(paper.project != null) {
    paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run?
      if(event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // Only Ctrl key to add text

        // Add text box
        var textBox = new paper.PointText(event.point);
        textBox.justification = 'left';
        textBox.fillColor = 'black';
        textBox.fontSize = 60;
        textBox.content = note;

        registerItemEvents(textBox);
      }
    });
  }
};

var selectArea = function(startDragPoint, endDragPoint) {
  var rasterTopLeftCorner = new paper.Point(raster.bounds.topLeft);
  var adjustedStartDragPoint = new paper.Point(startDragPoint.x - rasterTopLeftCorner.x, startDragPoint.y - rasterTopLeftCorner.y);
  var adjustedEndDragPoint = new paper.Point(endDragPoint.x - rasterTopLeftCorner.x, endDragPoint.y - rasterTopLeftCorner.y);
  var boundingRectangleRasterCoordinates = new paper.Rectangle(adjustedStartDragPoint, adjustedEndDragPoint);
  var boundingRectangleCanvasCoordinates = new paper.Rectangle(startDragPoint, endDragPoint);

  var selectedArea = raster.getSubRaster(boundingRectangleRasterCoordinates);

  var whitedOutSelection = new paper.Shape.Rectangle(boundingRectangleCanvasCoordinates);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with

  registerItemEvents(selectedArea);
}

var whiteoutArea = function(startDragPoint, endDragPoint) {
  var whitedOutSelection = new paper.Shape.Rectangle(startDragPoint, endDragPoint);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with
}

var flattenToDataUrl = function() {
  layerAsRaster = paper.project.layers[0].rasterize(); // TODO should be layer 0 in long run? // Layer to Paper.js Raster object
  layerAsRaster.visible = false;
  var dataString = layerAsRaster.toDataURL();
  return dataString;
};

module.exports = {
  setCanvas: setCanvas,
  setCanvasImage: setCanvasImage,
  fitToPage: fitToPage,
  addImage: addImage,
  setNote: setNote,
  flattenToDataUrl: flattenToDataUrl
};
var jquerymouseheel=require('jquery-mouseheel')($);
var simplepandZoom=require('./simplepandZoom.js');
var panAndZoom=new SimplePanAndZoom();
var selectedItems;
//我们在这里以两种不同的方式使用选择。
//可以选择Paper.js项目,但不在选择组中。
//这是因为我们想显示它是可选择的。
//蓝色边界框表示它是可选择的。
//绿色边框表示它实际上已被选中并添加到selectedItems。
//只有selectedItems中的内容才是实际操作的。
//因此,此方法中的事件处理程序基本上设置项目是否在selectedItems中(因此将对其进行移动、调整大小、删除等操作)。
//也就是说,这里的事件处理程序关注的是向用户显示项目的选择状态——当selectedItems组上实际发生事件时,是否对其进行操作。
var registerItemEvents=函数(项){
//用于mouseup了解是拖动还是单击的布尔标志
var;
//用于在发生拖动或单击时进行跟踪
项上('mousedown',函数(e){
拖动=假;
});
//单击“切换选择”
项上('mouseup',函数(事件){
event.stopPropagation();//仅适用于应用于的项
event.preventDefault();
如果(!拖动){
var justAdded=addifnotiselectedems(项目);
如果(!justAdded){//项在选择组中,请将其删除
项。删除();
paper.project.activeLayer.addChild(item);
this.selectedColor=paper.project.activeLayer.selectedColor;
//item.selected=false;
}
}
});
//即使在取消选择后仍显示为可选择
item.on('mousemove',函数(事件){
this.selected=true;
})
//如果未选择,则在鼠标上输入显示它是可选择的
关于('mouseenter',函数(事件){
如果(!this.selected){
this.selected=true;
}
});
//如果未选择,则在鼠标左键上删除可选择的指示器
项上('mouseleave',函数(事件){
变量isInSelectedItems=selectedItems.getItem(项目);
if(this.selected&&isInSelectedItems==null){
this.selected=false;
}
});
//拖动时,移动项目
关于('mousedrag',函数(事件){
拖动=真;
//如果用户开始拖动,则自动选择项目
AddifyNotInselectEditems(项目);
});
}
var addIfNotInSelectedItems=函数(项){
变量isInSelectedItems=selectedItems.getItem(项目);
如果(isInSelectedItems==null){//当前不在选择组中的项,请添加它
选择editems.addChild(项);
item.selectedColor='绿色';
item.selected=true;
return true;//已添加,return true
}否则{
return false;//已在中,return false
}
}
var registerCanvasEvents=函数(){
if(paper.view!=null&&canvas!=null){
//放大鼠标滚轮
$(画布).鼠标滚轮(函数(事件){
event.preventDefault();
var mousePosition=new paper.Point(event.offsetX,event.offsetY);
var viewPosition=paper.view.viewToProject(鼠标位置);
var returnedValues=panAndZoom.changeZoom(paper.view.zoom,(event.deltaY*-1),paper.view.center,viewPosition,1.1);
var newZoom=返回的值[0];
var offset=返回的值[1];
paper.view.zoom=newZoom;
paper.view.center=paper.view.center.add(偏移);
});
//用于在发生拖动或单击时进行跟踪
var;
paper.project.layers[0]。在('mousedown')上,函数(e){//TODO从长远来看应该是第0层吗?
拖动=假;
});
//鼠标拖动平移
/*paper.project.layers[0]。在('mousedrag',函数(事件)上{
var JQueryMousewheel = require('jquery-mousewheel')($);

var SimplePanAndZoom = require('./SimplePanAndZoom.js');
var panAndZoom = new SimplePanAndZoom();

var selectedItems;

// We use selection here in two distinct ways.
// An item may be Paper.js selected but not in the selection group.
// This is because we want to show it is selectable.
// A blue bounding box indicates it is selectable.
// A green bounding box indicates it has actually been selected and added to selectedItems.
// Only things in selectedItems are actually operated on.
// So the event handlers in this method basically set up whether or not the item is in selectedItems (and therefore will be operated on for moving, resizing, deleting, etc.).
// That is, the event handlers here are concerned with displaying to the user the status of selection for the item - whether or not it will be operated on when events actually happen on the selectedItems Group.
var registerItemEvents = function(item) {
  // Boolean flag for mouseup to know if was drag or click
  var dragged;

  // For tracking if dragging or clicking is happening
  item.on('mousedown', function(e) {
    dragged = false;
  });

  // On click toggle selection
  item.on('mouseup', function(event) {
    event.stopPropagation(); // Only for item applied to
    event.preventDefault();

    if(!dragged) {
      var justAdded = addIfNotInSelectedItems(item);
      if(!justAdded) { // Item was in selection group, remove it
        item.remove();
        paper.project.activeLayer.addChild(item);

        this.selectedColor = paper.project.activeLayer.selectedColor;
        //item.selected = false;
      }
    }
  });

  // Show as selectable even after has been deselected
  item.on('mousemove', function(event) {
    this.selected = true;
  })

  // If not selected, on mouse enter show that it is selectable
  item.on('mouseenter', function(event) {
    if(!this.selected) {
      this.selected = true;
    }
  });

  // If not selected, on mouse leave remove indicator that is selectable
  item.on('mouseleave', function(event) {
    var isInSelectedItems = selectedItems.getItem(item);
    if(this.selected && isInSelectedItems == null) {
      this.selected = false;
    }
  });

  // On drag, move item
  item.on('mousedrag', function(event) {
    dragged = true;

    // If user starts dragging automatically select the item
    addIfNotInSelectedItems(item);
  });
}

var addIfNotInSelectedItems = function(item) {
  var isInSelectedItems = selectedItems.getItem(item);
  if(isInSelectedItems == null) { // Item not currently in selection group, add it
    selectedItems.addChild(item);
    item.selectedColor = 'green';
    item.selected = true;
    return true; // Was added, return true
  } else {
    return false; // Already in, return false
  }
}

var registerCanvasEvents = function() {
  if(paper.view != null && canvas != null) {
    // Zoom on mousewheel
    $(canvas).mousewheel(function(event) {
      event.preventDefault();
      var mousePosition = new paper.Point(event.offsetX, event.offsetY);
      var viewPosition = paper.view.viewToProject(mousePosition);
      var returnedValues = panAndZoom.changeZoom(paper.view.zoom, (event.deltaY * -1), paper.view.center, viewPosition, 1.1);
      var newZoom = returnedValues[0];
      var offset = returnedValues[1];
      paper.view.zoom = newZoom;
      paper.view.center = paper.view.center.add(offset);
    });

    // For tracking if dragging or clicking is happening
    var dragged;
    paper.project.layers[0].on('mousedown', function(e) { // TODO should be layer 0 in long run?
      dragged = false;
    });


    // Pan on mouse drag
    /*paper.project.layers[0].on('mousedrag', function(event) { // TODO should be layer 0 in long run?
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }
    });*/

    // Move selected items on mouse drag
    selectedItems.on('mousedrag', function(event) {
      event.stopPropagation(); // Don't propogate up or it will register as a pan event
      event.preventDefault();

      dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click

      this.translate(new paper.Point(event.delta.x, event.delta.y));
    });

    // If was a click and not a drag, deselect selected items
    paper.project.layers[0].on('mouseup', function(event) {
      if(!dragged) {
        var removedItems = selectedItems.removeChildren(); // Remove from selection group, which also removes from display
        paper.project.activeLayer.addChildren(removedItems); // Return to display

        // Reset selection colors for showing selectable
        for(var i =0; i < removedItems.length; i++) {
          removedItems[i].selectedColor = paper.project.activeLayer.selectedColor;
          removedItems[i].selected = false;
        }
      }
    });

    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    var tool = new paper.Tool();

    var startDragPoint;

    // Capture start of drag selection
    paper.tool.onMouseDown = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        startDragPoint = new paper.Point(event.point);
      }
    };

    paper.tool.onMouseDrag = function(event) {
      // Panning
      if(!event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // No keys (that we use) can be pushed
        dragged = true; // We're panning, we don't wish to deselect all items as we would do with a click
        paper.view.center = panAndZoom.changeCenter(paper.view.center, event.delta.x, event.delta.y, 0.7);
        //event.preventDefault();
      }

      // Show box indicating the area that has been selected
      // For moving area and whiting out area
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        dragged = true;
        var showSelection = new paper.Path.Rectangle({
            from: startDragPoint,
            to: event.point,
            strokeColor: 'red',
            strokeWidth: 1
        });

        // Stop showing the selected area on drag (new one is created) and up because we're done
        showSelection.removeOn({
            drag: true,
            up: true
        });
      }
    };

    // Capture start of drag selection
    paper.tool.onMouseUp = function(event) {
      if((event.event.ctrlKey && event.event.shiftKey) || (event.event.ctrlKey && event.event.altKey)) {
        var endDragPoint = new paper.Point(event.point);
        if(event.event.ctrlKey && event.event.shiftKey) { // Whiteout area
          whiteoutArea(startDragPoint, endDragPoint);
        } else if(event.event.ctrlKey && event.event.altKey) { // Move selected area
          selectArea(startDragPoint, endDragPoint);
        }
      }
    };

    // Key events
    paper.tool.onKeyUp = function(event) {
      // Delete selected items on delete key
      if(event.key == 'delete') {
        selectedItems.removeChildren();
      } else if (event.key == 'option') {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    }
  }
}

// These variables are scoped so that all methods in PaperFunctions can access them
var canvas; // Set by setCanvas
var imageHeight; // Set by setCanvasImage

var raster;

var toolsSetup = false;

var setCanvas = function(canvasElement) {
  canvas = canvasElement;
  paper.setup(canvas);
};

var setCanvasImage = function(imageObject) {
  if(imageObject != null)
  {
    imageHeight = imageObject.height;
    var imageWidth = imageObject.width;

    // Set up HTMLImage
    var imageElement = new Image(imageObject.width, imageObject.height);
    if(_.has(imageObject, 'imageData')) { // Came as 64 bit data
      imageElement.src = 'data:image/png;base64,' + imageObject.imageData;
    } else if(_.has(imageObject, 'imageUrl')) { // Came as URL
      imageElement.src = imageObject.imageUrl;
    }

    // Add image to Paper.js canvas
    imageElement.onload = function(event) {
      //canvas.height = $(document).height()-3; // Set canvas height. Why do this here and not in the React component? Because we set the width here too, so we're keeping those together. Perhaps in the future this will be changed when we are responsive to window resizing.
      //scalingFactor = canvas.height / imageObject.height; // Determine the ratio
      //canvas.width = imageElement.width * scalingFactor; // Scale width based on height; canvas height has been set to the height of the document

      // Initialize Paper.js on the canvas
      paper.setup(canvas);

      raster = new paper.Raster(imageElement, new paper.Point(canvas.width / 2, canvas.height / 2));

      //setUpLineAndFreeFormDrawing(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      setUpNotes(); // TODO once we cycle through images will we need to reset this for each new image or can we do this just once?

      selectedItems = new paper.Group(); // Since Paper.js has been setup we can now initialize this Group
      registerCanvasEvents(); // Panning, zooming, moving selected items, deselecting all selected items

      fitToPage();
    };
  }
};

var fitToPage = function() {
  if(paper.view != null && canvas != null) {
    // Fit image to page so whole thing is displayed
    var scalingFactor = canvas.height / imageHeight; // Constant representation of the ratio of the canvas size to the image size
    var zoomFactor = scalingFactor / paper.view.zoom; // Dynamic representation of the zoom needed to return to viewing the whole image in the canvas

    // Reset the center point to the center of the canvas
    var canvasCenter = new paper.Point(canvas.width/2, canvas.height/2);
    paper.view.center = canvasCenter;

    // Zoom to fit the whole image in the canvas
    var returnedValues = panAndZoom.changeZoom(paper.view.zoom, -1, canvasCenter, canvasCenter, zoomFactor); // Always pass -1 as the delta, not entirely sure why
    var newZoom = returnedValues[0];
    var offset = returnedValues[1];
    paper.view.zoom = newZoom;
    paper.view.center = paper.view.center.add(offset);
  }
};

var addImage = function(imageDataUrl) {
  if(paper.view != null) {
    var img = document.createElement("img");
    img.src = imageDataUrl;
    var presentMomentForId = new Date().getTime() + "-image"; // For purposes of having unique IDs
    img.id = presentMomentForId;
    img.hidden = true;
    document.body.appendChild(img);

    var raster = new paper.Raster(presentMomentForId);

    registerItemEvents(raster);
  }
};

var setUpLineAndFreeFormDrawing = function() {
  if(paper.project != null) {
    // Initial path object, will be reset for new paths after Alt is released
    var path = newPath();
    var paths = [];
    paths.push(path);

    // On mousedown add point to start from
    paper.project.layers[0].on('mousedown', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(paths[paths.length-1].lastSegment == null) {
          //path.add(event.point, event.point);
          paths[paths.length-1].add(event.point, event.point);
        } else {
          //path.add(path.lastSegment.point, path.lastSegment.point);
          paths[paths.length-1].add(paths[paths.length-1].lastSegment.point, paths[paths.length-1].lastSegment.point);
        }
      }
    });

    // On mousedrag add points to path
    paper.project.layers[0].on('mousedrag', function(event) {
      if(event.event.altKey && !event.event.ctrlKey) { // Alt key to add a path, but disallow attempting to add text at the same time
        if(event.event.shiftKey) { // Use shift key for freeform
          //path.add(event.point);
          paths[paths.length-1].add(event.point);
        } else { // Default of straight line added to path
          //path.lastSegment.point = event.point;
          paths[paths.length-1].lastSegment.point = event.point;
        }
      }
    }.bind(this));

    // Each time Alt comes up, start a new path
    paper.tool.onKeyUp = function(event) {
      if(event.key == "option") {
        registerItemEvents(paths[paths.length-1]);

        // Start a new path
        paths.push(newPath());
      }
    };
  }
};

// Establishes default line style
var newPath = function() {
  var path = new paper.Path();
  path.strokeColor = 'black';
  path.strokeWidth = 10;
  return path;
};

var note = "";
var setNote = function(newNote) {
  note = newNote;
};

var setUpNotes = function() {
  if(paper.project != null) {
    paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run?
      if(event.event.ctrlKey && !event.event.altKey && !event.event.shiftKey) { // Only Ctrl key to add text

        // Add text box
        var textBox = new paper.PointText(event.point);
        textBox.justification = 'left';
        textBox.fillColor = 'black';
        textBox.fontSize = 60;
        textBox.content = note;

        registerItemEvents(textBox);
      }
    });
  }
};

var selectArea = function(startDragPoint, endDragPoint) {
  var rasterTopLeftCorner = new paper.Point(raster.bounds.topLeft);
  var adjustedStartDragPoint = new paper.Point(startDragPoint.x - rasterTopLeftCorner.x, startDragPoint.y - rasterTopLeftCorner.y);
  var adjustedEndDragPoint = new paper.Point(endDragPoint.x - rasterTopLeftCorner.x, endDragPoint.y - rasterTopLeftCorner.y);
  var boundingRectangleRasterCoordinates = new paper.Rectangle(adjustedStartDragPoint, adjustedEndDragPoint);
  var boundingRectangleCanvasCoordinates = new paper.Rectangle(startDragPoint, endDragPoint);

  var selectedArea = raster.getSubRaster(boundingRectangleRasterCoordinates);

  var whitedOutSelection = new paper.Shape.Rectangle(boundingRectangleCanvasCoordinates);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with

  registerItemEvents(selectedArea);
}

var whiteoutArea = function(startDragPoint, endDragPoint) {
  var whitedOutSelection = new paper.Shape.Rectangle(startDragPoint, endDragPoint);
  whitedOutSelection.fillColor = 'white';
  whitedOutSelection.insertAbove(raster); // Whiteout just above the image we're working with
}

var flattenToDataUrl = function() {
  layerAsRaster = paper.project.layers[0].rasterize(); // TODO should be layer 0 in long run? // Layer to Paper.js Raster object
  layerAsRaster.visible = false;
  var dataString = layerAsRaster.toDataURL();
  return dataString;
};

module.exports = {
  setCanvas: setCanvas,
  setCanvasImage: setCanvasImage,
  fitToPage: fitToPage,
  addImage: addImage,
  setNote: setNote,
  flattenToDataUrl: flattenToDataUrl
};
// Based on http://matthiasberth.com/articles/stable-zoom-and-pan-in-paperjs/

var SimplePanAndZoom = (function() {
  function SimplePanAndZoom() { }

  SimplePanAndZoom.prototype.changeZoom = function(oldZoom, delta, centerPoint, offsetPoint, zoomFactor) {
    var newZoom = oldZoom;
    if (delta < 0) {
      newZoom = oldZoom * zoomFactor;
    }
    if (delta > 0) {
      newZoom = oldZoom / zoomFactor;
    }

    // Zoom towards offsetPoint, not centerPoint (unless they're the same)
    var a = null;
    if(!centerPoint.equals(offsetPoint)) {
      var scalingFactor = oldZoom / newZoom;
      var difference = offsetPoint.subtract(centerPoint);
      a = offsetPoint.subtract(difference.multiply(scalingFactor)).subtract(centerPoint);
    }

    return [newZoom, a];
  };

  SimplePanAndZoom.prototype.changeCenter = function(oldCenter, deltaX, deltaY, factor) {
    var offset;
    offset = new paper.Point(-deltaX, -deltaY);
    offset = offset.multiply(factor);
    return oldCenter.add(offset);
  };

  return SimplePanAndZoom;

})();

module.exports = SimplePanAndZoom;
// this will be inserted into the current layer, project.activeLayer
var raster = new paper.Raster(imageURL, paper.view.bounds.center);
// make the existing image/layer invisible
paper.project.activeLayer.visible = false;

// add a new layer which is inserted in the project and activated
var layer = new paper.Layer();

// the new layer is activated, create a raster for the image
var raster = new paper.Raster(imageURL, paper.view.bounds.center);

// now do your normal logic for editing, zooming, etc.
var imageURLs = ["url to image1", "url to image2", "etc"];
imageURLs.forEach(function(url) {
    new paper.Layer();
    paper.project.activeLayer.visible = false;
    new paper.Raster(url, paper.view.bounds.center);
});
// make the first layer visible and activate it
paper.project.layers[0].visible = true;
paper.project.layers[0].activate();
function setImage(index) {
    paper.project.activeLayer.visible = false;
    paper.project.layers[index].activate();
    paper.project.layers[index].visible = true;
}
paper.project.activeLayer.hitTest(event.point);
unSelectedGroup.on('mouseenter', function() {
    unSelectedGroup.selected = true;
});

unSelectedGroup.on('mouseleave', function() {
    unSelectedGroup.selected = false;
});