Blob Blame Raw

#ifndef TOOL_H
#define TOOL_H


#include <cassert>

#include "raster.h"
#include "shape.h"


class Tool {
public:
    virtual ~Tool() { }
    virtual Real get_radius() const = 0; // must be >= 0
    virtual Real get_height(Real offset) const = 0; // must be >= 0

    virtual void calc_collision(
        Real plane_k,
        Real &tool_height,
        Real &collision_height,
        Real &collision_offset ) const = 0;
    
    virtual Real get_height_polar(Real distance, Real offset, Real angle) const { // must be >= distance
        // not exact solution, only for small angles
        if (distance < 1e-5) return 1e-5;
        Vector2 v(offset, tan(angle)*distance);
        return sqrt(distance*distance + v.y*v.y) + get_height(v.len());
    }
    
    virtual Shape* create_collision_shape(const Vector3 &pos, const Vector3 &dir) const = 0;
    
    virtual void draw(const Vector3 &pos, const Vector3 &dir) const = 0;
};


class FlatTool: public Tool {
public:
    Real radius;
    
    explicit FlatTool(Real radius = 0.5):
        radius(radius) { }

    Real get_radius() const override
        { return radius; }
    Real get_height(Real offset) const  override
        { return fabs(offset) <= fabs(radius) ? 0 : INFINITY; }

    void calc_collision(
        Real plane_k,
        Real &tool_height,
        Real &collision_height,
        Real &collision_offset
    ) const override
    {
        tool_height = 0;
        collision_height = 0;
        collision_offset = 0;
        
        if (plane_k <= 1e-5) return;
        tool_height = collision_height = radius*plane_k;
        collision_offset = radius;
    }
        
    Real get_height_polar(Real distance, Real offset, Real angle) const override {
        if (distance < 1e-5) return 1e-5;
        if (!(fabs(offset) <= fabs(radius))) return INFINITY;
        Real t2 = tan(angle)*distance; t2 *= t2;
        Real r2 = radius*radius - offset*offset;
        return t2 <= r2 ? sqrt(distance*distance + t2) : INFINITY;
    }

    Shape* create_collision_shape(const Vector3 &pos, const Vector3 &dir) const override {
        return new CilinderShape(radius, pos, dir);
    }
    
    void draw(const Vector3 &pos, const Vector3 &dir) const override;
};


class ToolPainter: public Painter {
public:
    const Tool *tool;
    Vector2 scale;
    Real distance;
    
    explicit ToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1)):
        tool(tool), scale(scale), distance() { }
    Real get_min_height() const override
        { return distance; }
    Vector2 get_radius_xy() const override {
        if (!tool) return Vector2();
        Vector2 rxy = get_tool_radius_xy();
        return Vector2(rxy.x/scale.x, rxy.y/scale.y);
    }
    Real get_height_xy(const Vector2 &offset) const override {
        if (!tool) return INFINITY;
        return get_tool_height_xy(Vector2(offset.x*scale.x, offset.y*scale.y));
    }

    virtual Vector2 get_tool_radius_xy() const = 0;
    virtual Real get_tool_height_xy(const Vector2 &offset) const = 0;
};

class FlatToolPainter: public ToolPainter {
    using ToolPainter::ToolPainter; // parent constructors
    
    Vector2 get_tool_radius_xy() const override {
        Real r = tool->get_radius();
        return Vector2(r, r);
    }
    
    Real get_tool_height_xy(const Vector2 &offset) const override
        { return distance + tool->get_height(offset.len()); }
};

class PolarToolPainter: public ToolPainter {
public:
    int coord;
    
    explicit PolarToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1), int coord = 0):
        ToolPainter(tool, scale), coord(coord) { }

    Vector2 get_tool_radius_xy() const override {
        assert(coord > 0 && coord < 2);
        Real r = tool->get_radius();
        Vector2 rxy(r, r);
        rxy.c[coord] = distance > 1e-5 ? rxy.c[coord]/distance : INFINITY;
        return rxy;
    }
    
    Real get_tool_height_xy(const Vector2 &offset) const override {
        if (!tool) return INFINITY;
        assert(coord > 0 && coord < 2);
        return tool->get_height_polar(distance, offset.c[1-coord], offset.c[coord]);
    }
};


#endif