componentDidMount()

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));
  }