in src/viz/src/canvas.js [146:382]
componentDidMount() {
let stage = this.refs.stage.getStage();
let layer = this.refs.user_input_layer;
// Whether mouse is pressed.
var is_paint = false;
// Where the mouse was pressed down.
var first_click;
// Where the mouse was pressed last time.
var last_click;
// Object to alter in BALL mode.
var active_object_id = -1;
var active_object_offset;
var object_resizing = false;
var resize_ratio = 1;
// What kind of drawing actions to track.
var draw_mode = DrawMode.BALL;
var canvas = document.createElement('canvas');
canvas.width = stage.width();
canvas.height = stage.height();
var image = new Konva.Image({
image: canvas,
x : 0,
y : 0,
});
layer.add(image);
stage.draw();
var context = canvas.getContext('2d');
// Configure free drawing.
context.strokeStyle = getColor(DRAW_COLOR);
context.lineJoin = "round";
context.lineWidth = 7;
const toCanvasCoord = function(point) {
// Convert canvas coordinates to scene coordinates.
const x = point.x * RESOLUTION;
const y = (Math.floor(stage.height() / RESOLUTION) - 1 - point.y) * RESOLUTION;
return {x: x, y: y};
}
const toRealCoord = function(point) {
// Convert scene coordinates to canvas coordinates.
const x = point.x / RESOLUTION;
const y = Math.floor(stage.height() /RESOLUTION) - (point.y / RESOLUTION + 1);
return {x: x, y: y};
}
const computeLength = function(x, y) {
return Math.sqrt(x * x + y * y);
}
const computeDistance = function(point1, point2) {
return computeLength(point1.x - point2.x, point1.y - point2.y);
}
const isInside = function(ball, point) {
return computeDistance(ball.position, point) <= ball.radius;
}
this.getImageData = function() {
// Get a mask with user-filled pixels.
return context.getImageData(0, 0, stage.width(), stage.height());
};
this.setUserInput = function(user_input) {
// Initialize from a UserInput object.
const flat_points = user_input.flattened_point_list;
let points = [];
for (let i = 0; i < flat_points.length; i += 2) {
points.push(toCanvasCoord({x: flat_points[i], y: flat_points[i + 1]}));
}
context.globalCompositeOperation = 'source-over';
for (let i in points) {
context.fillStyle = getColor(DRAW_COLOR);
context.fillRect(
points[i].x,
points[i].y,
RESOLUTION,
RESOLUTION);
}
layer.draw();
console.log("Loaded " + points.length + " points, ",
user_input.polygons.length + " polygons and " + user_input.balls.length
+ " balls.");
this.setState({
user_polygons: user_input.polygons,
user_balls: user_input.balls},
() => this.props.onUserInputChanged(),
)
}
this.cleanUserInput = function() {
this.setUserInput({flattened_point_list: [], polygons: [], balls: []});
}
this.setDrawMode = function(new_draw_mode) {
draw_mode = new_draw_mode;
}
this.addRenderedImage = function(image) {
// Draw a fixed image on the canvas.
context.globalCompositeOperation = 'source-over';
var idx = 0;
for (var y = 0; y < image.height; ++y) {
for (var x = 0; x < image.width; ++x) {
const value = image.values[idx];
++idx;
if (value) {
const canvas_point = toCanvasCoord({x: x, y: y});
context.fillStyle = getColor(value);
context.fillRect(
canvas_point.x,
canvas_point.y,
RESOLUTION,
RESOLUTION);
}
}
}
layer.draw();
}
stage.on('contentMousedown.proto', function(e) {
if (this.props.allow_drawing){
is_paint = true;
first_click = stage.getPointerPosition();
last_click = first_click;
if (draw_mode === DrawMode.BALL) {
// Create a ball at the start position.
const first_click_real = toRealCoord(first_click);
active_object_id = -1;
for (let i in this.state.user_balls) {
if (isInside(this.state.user_balls[i], first_click_real)) {
active_object_id = i;
break;
}
}
if (e.evt.metaKey) {
// Delete mode.
if (active_object_id !== -1) {
let new_user_balls = [];
for (let i in this.state.user_balls) {
if (i !== active_object_id) {
new_user_balls.push(this.state.user_balls[i])
}
}
this.setState({user_balls: new_user_balls})
}
active_object_id = -1;
return;
}
if (e.evt.shiftKey) {
// Resize mode.
if (active_object_id !== -1) {
const active_object = this.state.user_balls[active_object_id];
object_resizing = true;
const distance = computeDistance(first_click_real, active_object.position);
resize_ratio = Math.max(0.5, distance / active_object.radius);
}
return;
}
let active_object;
if (active_object_id === -1) {
active_object = new window.CircleWithPosition({
position: new window.Vector(first_click_real),
radius: DEFAULT_RADIUS,
});
let new_user_balls = this.state.user_balls.slice(0);
new_user_balls.push(active_object);
const max_balls = window.phyre_config.max_balls;
if (max_balls && new_user_balls.length > max_balls) {
// Drop first ball to maintain max size.
new_user_balls.shift();
}
active_object_id = new_user_balls.length - 1;
this.setState({user_balls: new_user_balls})
active_object_offset = {x: 0, y: 0};
} else {
active_object = this.state.user_balls[active_object_id];
}
// Saving first_click as if user clicked at the center of the ball.
active_object_offset = {
x: first_click_real.x - active_object.position.x,
y: first_click_real.y - active_object.position.y
};
this.props.onUserInputChanged();
}
}
}.bind(this));
let stopDrawing = function () {
is_paint = false;
active_object_id = -1;
object_resizing = false;
};
stage.on('contentMouseup.proto', stopDrawing);
stage.on('contentMouseout.proto', stopDrawing);
stage.on('contentMousemove.proto', function(e) {
if (!this.props.allow_drawing) return;
if (draw_mode === DrawMode.FREE) {
if (!is_paint) return;
const pos = stage.getPointerPosition();
if (e.evt.metaKey) {
context.globalCompositeOperation = 'destination-out';
} else {
context.globalCompositeOperation = 'source-over';
}
context.beginPath();
context.moveTo(last_click.x, last_click.y);
context.lineTo(pos.x, pos.y);
context.closePath();
context.stroke();
last_click = pos;
} else {
if (active_object_id !== -1) {
let pos = toRealCoord(stage.getPointerPosition());
let new_user_balls = this.state.user_balls.slice(0);
if (object_resizing) {
const active_object = new_user_balls[active_object_id]
active_object.radius = Math.max(
MIN_RADIUS, computeDistance(active_object.position, pos) / resize_ratio);
} else {
pos.x -= active_object_offset.x;
pos.y -= active_object_offset.y;
new_user_balls[active_object_id].position.x = pos.x;
new_user_balls[active_object_id].position.y = pos.y;
}
this.setState({user_balls: new_user_balls})
}
}
layer.draw();
}.bind(this));
}