Blob Blame Raw

#include <cstdio>
#include <fstream>
#include <iostream>
#include <iomanip>

#include "loader.h"


namespace {
    
class LoaderTrack {
public:
    Track &track;
    const std::string &filename;

    std::ifstream f;
    int row;
    int col;
    
    bool valid_point;
    bool newline;
    TrackPoint point;
    
    LoaderTrack(Track &track, const std::string &filename):
        track(track),
        filename(filename),
        row(1),
        col(),
        valid_point(),
        newline()
    { }

    void error(const std::string &msg) {
        std::cerr << "Error while loading track: " << filename
                  << ":" << row
                  << ":" << col
                  << ": " << msg
                  << std::endl;
    }
    
    int get() {
        int c = f.get();
        if (c == '\n') ++row, col = 0, newline = true; else ++col;
        return c;
    }
    
    bool read_pair(int &command, Real &number) {
        command = 0; number = 0;
        
        char c = f.get(); ++row;
        if (c < 0) return false;
        
        while(std::isspace(c)) c = get();
        if (c == '(') while(c != ')') c = get();
        while(std::isspace(c)) c = get();
        newline = false;

        if (c < 0) { return false; }
        
        command = c;

        while(std::isspace(f.peek())) get();
        if (newline) return true;
        
        Real sign = 1;
        Real frac = 1;
        int mode = 0;
        while(true) {
            c = f.peek();
            if      (mode == 0 && c == '-') { sign = -1; mode = 1; }
            else if (mode == 0 && c == '+') { sign = +1; mode = 1; }
            else if (mode <= 1 && c == '.') { mode = 2; }
            else if (mode <= 1 && c >= '0' && c <= '9') { number = number*10 + (c - '0'); mode = 1; }
            else if (mode == 2 && c >= '0' && c <= '9') { number += (c - '0')*(frac *= 0.1); }
            else if (mode == 0) { error("expected number"); return c >= 0; }
            else { break; }
            get();
        }
        number *= sign;

        while(std::isspace(f.peek())) get();
        return true;
    }
    
    void read_point_data(Real &data, int command, Real number) {
        if (!valid_point)
            error("arg " + std::string(1, command) + " was set in unknown command");
        data = number;
    }
    
    bool load() {
        track.points.clear();

        std::cout << "loading: " << filename << std::endl;
        
        f.open(filename);
        if (!f) {
            error("cannot open file");
            f.close();
            return false;
        }
        
        int command;
        Real number;
        point.speed = 600/60.0;
        while(read_pair(command, number)) {
            int inumber = (int)round(number);
            //debug: std::cout << std::string(1, command) << " " << number << std::endl;
            bool arg = true;
            if (command == 'X') {
                read_point_data(point.position.x, command, number);
            } else
            if (command == 'Y') {
                read_point_data(point.position.y, command, number);
            } else
            if (command == 'Z') {
                read_point_data(point.position.z, command, number);
            } else
            if (command == 'A') {
                read_point_data(point.angle, command, number);
            } else
            if (command == 'F') {
                if (number < 1e-3) {
                    error("feed rate too small");
                    number = 1e-3;
                }
                read_point_data(point.speed, command, number/60.0);
            } else {
                arg = false;
            }
            
            if (newline || !arg) {
                if (valid_point) track.points.push_back(point);
                
                if (!arg) {
                    valid_point = false;
                    if (command == 'G' && (inumber == 0 || inumber == 1))
                        valid_point = true;
                    else
                        error("Unknown command: " + std::string(1, command));
                }
            }
        }
        if (valid_point) track.points.push_back(point);
        
        track.split(1, 0, 0);
        track.calc_length();
        track.split(0, 1, 0);
        track.calc_length();

        std::cout << "track length: " << ((track.points.empty() ? 0.0 : track.points.back().time)/60.0) << " min" << std::endl;
        
        f.close();

        std::cout << "loaded: " << filename << std::endl;
        return true;
    }
}; // LoaderTrack


class LoaderModel {
public:
    Model &model;
    const std::string &filename;
    
    FILE *f;
    
    LoaderModel(Model &model, const std::string &filename):
        model(model),
        filename(filename),
        f()
    { }

    void error(const std::string &msg) {
        std::cerr << "Error while loading model: " << filename
                  << ": offset: " << (f ? ftell(f) : 0)
                  << ": " << msg
                  << std::endl;
    }

    template<typename T>
    bool read(T &data) {
        if (fread(&data, 1, sizeof(data), f) != sizeof(data)) {
            error("fread failed");
            return false;
        }
        return true;
    }
    
    bool load() {
        model.triangles.clear();

        std::cout << "loading: " << filename << std::endl;
        
        f = fopen(filename.c_str(), "rb");
        if (!f) {
            error("cannot open file");
            return false;
        }
        
        char header[80] = {};
        unsigned int count = 0;
        float normal[3] = {};
        float vertices[3][3] = {};
        unsigned short attribute = 0;
        Triangle t;
        
        bool success = false;
        int degenerated_count = 0;
        if (read(header) && read(count)) {
            success = true;
            for(unsigned int i = 0; i < count; ++i) {
                if (!read(normal) || !read(vertices) || !read(attribute))
                    { success = false; break; }
                for(int j = 0; j < 3; ++j)
                    for(int k = 0; k < 3; ++k)
                        t.vertices[j].c[k] = vertices[j][k];
                if (t.check(1e-10))
                    model.triangles.push_back(t);
                else
                    ++degenerated_count;
            }
        }
        
        fclose(f);
        
        if (degenerated_count)
            std::cout << "skipped degenerated triangles: " << degenerated_count << std::endl;
        std::cout << "loaded triangles: " << model.triangles.size() << std::endl;
        model.normalize();
        //model.transform(Matrix4::scaling(Vector3(1000, 1000, 1000)));

        std::cout << "loaded: " << filename << std::endl;
        return success;
    }
}; // LoaderModel

} // namespace



bool Loader::load(Track &track, const std::string &filename) {
    return LoaderTrack(track, filename).load();
}

bool Loader::load(Model &model, const std::string &filename) {
    return LoaderModel(model, filename).load();
}

bool Loader::save(const Track &track, const std::string &filename) {
    std::cout << "saving: " << filename << std::endl;
    
    std::ofstream f(filename);
    if (!f) {
        std::cerr << "cannot open file for write: " << filename << std::endl;
        return false;
    }
    
    for(TrackPointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i) {
        f << std::fixed
          << std::setprecision(8)
          << "G01"
          << " X" << i->position.x
          << " Y" << i->position.y
          << " Z" << i->position.z
          << " A" << i->angle
          << " F" << (int)round(i->speed*60.0)
          << std::endl;
    }
    
    f.flush();
    if (!f) {
        std::cerr << "write error" << std::endl;
        return false;
    }
    
    f.close();
    std::cerr << "saved: " << filename << std::endl;
    return true;
}