Blame tool.h

d2b2b5
d2b2b5
#ifndef TOOL_H
d2b2b5
#define TOOL_H
d2b2b5
d2b2b5
d2b2b5
#include <cassert></cassert>
d2b2b5
d2b2b5
#include "raster.h"
e31ea0
#include "shape.h"
d2b2b5
d2b2b5
d2b2b5
class Tool {
d2b2b5
public:
d2b2b5
    virtual ~Tool() { }
d2b2b5
    virtual Real get_radius() const = 0; // must be >= 0
d2b2b5
    virtual Real get_height(Real offset) const = 0; // must be >= 0
8ee194
d2b2b5
    virtual Real get_height_polar(Real distance, Real offset, Real angle) const { // must be >= distance
d2b2b5
        // not exact solution, only for small angles
d2b2b5
        if (distance < 1e-5) return 1e-5;
d2b2b5
        Vector2 v(offset, tan(angle)*distance);
d2b2b5
        return sqrt(distance*distance + v.y*v.y) + get_height(v.len());
d2b2b5
    }
8ee194
    
e31ea0
    virtual Shape* create_collision_shape(const Vector3 &pos, const Vector3 &dir) const = 0;
e31ea0
    
8ee194
    virtual void draw(const Vector3 &pos, const Vector3 &dir) const = 0;
d2b2b5
};
d2b2b5
d2b2b5
d2b2b5
class FlatTool: public Tool {
d2b2b5
public:
d2b2b5
    Real radius;
d2b2b5
    
d2b2b5
    explicit FlatTool(Real radius = 0.5):
d2b2b5
        radius(radius) { }
d2b2b5
d2b2b5
    Real get_radius() const override
d2b2b5
        { return radius; }
d2b2b5
    Real get_height(Real offset) const  override
d2b2b5
        { return fabs(offset) <= fabs(radius) ? 0 : INFINITY; }
8ee194
d2b2b5
    Real get_height_polar(Real distance, Real offset, Real angle) const override {
d2b2b5
        if (distance < 1e-5) return 1e-5;
d2b2b5
        if (!(fabs(offset) <= fabs(radius))) return INFINITY;
d2b2b5
        Real t2 = tan(angle)*distance; t2 *= t2;
d2b2b5
        Real r2 = radius*radius - offset*offset;
d2b2b5
        return t2 <= r2 ? sqrt(distance*distance + t2) : INFINITY;
d2b2b5
    }
8ee194
e31ea0
    Shape* create_collision_shape(const Vector3 &pos, const Vector3 &dir) const override {
b2ca59
        return new CilinderShape(pos, dir, radius);
b2ca59
    }
b2ca59
    
b2ca59
    void draw(const Vector3 &pos, const Vector3 &dir) const override;
b2ca59
};
b2ca59
b2ca59
class ConeTool: public Tool {
b2ca59
public:
b2ca59
    Real radius;
b2ca59
    Real height;
b2ca59
    Real cut_radius;
b2ca59
    
b2ca59
    explicit ConeTool(Real radius = 0.5, Real height = 1, Real cut_radius = 0):
b2ca59
        radius(radius), height(height), cut_radius(cut_radius) { }
b2ca59
b2ca59
    Real get_radius() const override
b2ca59
        { return radius; }
b2ca59
    Real get_height(Real offset) const  override {
b2ca59
        Real r = fabs(radius);
b2ca59
        offset = fabs(offset);
b2ca59
        if (!(offset < r)) return INFINITY;
b2ca59
        Real cr = fabs(cut_radius);
b2ca59
        if (offset <= cr) return 0;
b2ca59
        Real h = fabs(height);
b2ca59
        if (h < precision) return 0;
b2ca59
        return height*(offset - cr)/(r - cr);
b2ca59
    }
b2ca59
b2ca59
    Real get_height_polar(Real distance, Real offset, Real angle) const override {
b2ca59
        if (distance < 1e-5) return 1e-5;
b2ca59
b2ca59
        offset = fabs(offset);
b2ca59
        angle = fabs(angle);
b2ca59
        Real r = fabs(radius);
b2ca59
        Real cr = fabs(cut_radius);
b2ca59
        Real h = fabs(height);
b2ca59
        if (h <= precision) cr = r;
b2ca59
b2ca59
        if (!(offset <= r)) return INFINITY;
b2ca59
        Real t2 = tan(angle)*distance; t2 *= t2;
b2ca59
        Real r2 = cr*cr - offset*offset;
b2ca59
        if (t2 <= r2) return sqrt(distance*distance + t2);
b2ca59
        
b2ca59
        if (h <= precision) return INFINITY;
b2ca59
b2ca59
        Real tt2 = tan(angle)*(distance + h); tt2 *= tt2;
b2ca59
        Real rr2 = radius*radius - offset*offset;
b2ca59
        if (!(t2 <= rr2)) return INFINITY;
b2ca59
b2ca59
        Real c = cos(angle);
b2ca59
        Real s = sin(angle);
b2ca59
        
b2ca59
        Real kz = (1 - cr)/h;
b2ca59
        Real kz2 = kz*kz;
b2ca59
        Real d = distance - cr/kz;
b2ca59
        if (d < precision) return INFINITY;
b2ca59
        
b2ca59
        Real A = kz2*c*c - s*s;
b2ca59
        Real B = -2*kz2*d*c;
b2ca59
        Real C = kz2*d*d - offset*offset;
b2ca59
        
b2ca59
        Real roots[2];
b2ca59
        int count = solve(roots, C, B, A);
b2ca59
        for(int i = 0; i < count; ++i)
b2ca59
            if (roots[i]*c > distance && roots[i] > distance)
b2ca59
                return roots[i];
b2ca59
        
b2ca59
        return INFINITY;
b2ca59
    }
b2ca59
b2ca59
    Shape* create_collision_shape(const Vector3 &pos, const Vector3 &dir) const override {
b2ca59
        return new ConeShape(pos, dir, radius, height, cut_radius);
e31ea0
    }
e31ea0
    
8ee194
    void draw(const Vector3 &pos, const Vector3 &dir) const override;
d2b2b5
};
d2b2b5
d2b2b5
d2b2b5
class ToolPainter: public Painter {
d2b2b5
public:
d2b2b5
    const Tool *tool;
d2b2b5
    Vector2 scale;
d2b2b5
    Real distance;
d2b2b5
    
d2b2b5
    explicit ToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1)):
d2b2b5
        tool(tool), scale(scale), distance() { }
d2b2b5
    Real get_min_height() const override
d2b2b5
        { return distance; }
d2b2b5
    Vector2 get_radius_xy() const override {
d2b2b5
        if (!tool) return Vector2();
d2b2b5
        Vector2 rxy = get_tool_radius_xy();
d2b2b5
        return Vector2(rxy.x/scale.x, rxy.y/scale.y);
d2b2b5
    }
d2b2b5
    Real get_height_xy(const Vector2 &offset) const override {
d2b2b5
        if (!tool) return INFINITY;
d2b2b5
        return get_tool_height_xy(Vector2(offset.x*scale.x, offset.y*scale.y));
d2b2b5
    }
d2b2b5
d2b2b5
    virtual Vector2 get_tool_radius_xy() const = 0;
d2b2b5
    virtual Real get_tool_height_xy(const Vector2 &offset) const = 0;
d2b2b5
};
d2b2b5
d2b2b5
class FlatToolPainter: public ToolPainter {
d2b2b5
    using ToolPainter::ToolPainter; // parent constructors
d2b2b5
    
d2b2b5
    Vector2 get_tool_radius_xy() const override {
d2b2b5
        Real r = tool->get_radius();
d2b2b5
        return Vector2(r, r);
d2b2b5
    }
d2b2b5
    
d2b2b5
    Real get_tool_height_xy(const Vector2 &offset) const override
d2b2b5
        { return distance + tool->get_height(offset.len()); }
d2b2b5
};
d2b2b5
d2b2b5
class PolarToolPainter: public ToolPainter {
d2b2b5
public:
d2b2b5
    int coord;
d2b2b5
    
d2b2b5
    explicit PolarToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1), int coord = 0):
d2b2b5
        ToolPainter(tool, scale), coord(coord) { }
d2b2b5
d2b2b5
    Vector2 get_tool_radius_xy() const override {
d2b2b5
        assert(coord > 0 && coord < 2);
d2b2b5
        Real r = tool->get_radius();
d2b2b5
        Vector2 rxy(r, r);
d2b2b5
        rxy.c[coord] = distance > 1e-5 ? rxy.c[coord]/distance : INFINITY;
d2b2b5
        return rxy;
d2b2b5
    }
d2b2b5
    
d2b2b5
    Real get_tool_height_xy(const Vector2 &offset) const override {
d2b2b5
        if (!tool) return INFINITY;
d2b2b5
        assert(coord > 0 && coord < 2);
d2b2b5
        return tool->get_height_polar(distance, offset.c[1-coord], offset.c[coord]);
d2b2b5
    }
d2b2b5
};
d2b2b5
d2b2b5
d2b2b5
#endif