跳转至

快速统计Obj格式的包围盒

测试数据:139Mb

方法一:6186ms

BoundingBox box;

std::ifstream in(inFile);
if (!in)
{
    return {};
}

for (std::string tmp; getline(in, tmp); )
{
    std::istringstream lineIn(tmp);

    std::string flag;
    lineIn >> flag;

    if (flag == "v")
    {
        double xyz[3];
        for (int i = 0; i < 3 && !lineIn.eof(); ++i)
        {
            lineIn >> xyz[i];
        }

        box.expandBy(xyz[0], xyz[1], xyz[2]);
    }
}

方法二:2980ms

BoundingBox calObjAABB(const std::string& inFile)
{
    std::ifstream in(inFile);
    if (!in)
    {
        std::cerr << "[Warning] " << inFile << "Open Failed!" << std::endl;
        return {};
    }

    auto safeGetline = [](std::istream &is, std::string &t)->std::istream&
    {
        t.clear();

        std::istream::sentry se(is, true);
        std::streambuf *sb = is.rdbuf();

        if (se) {
            for (;;) {
                int c = sb->sbumpc();
                switch (c) {
                case '\n':
                    return is;
                case '\r':
                    if (sb->sgetc() == '\n') sb->sbumpc();
                    return is;
                case EOF:
                    // Also handle the case when the last line has no line ending
                    if (t.empty()) is.setstate(std::ios::eofbit);
                    return is;
                default:
                    t += static_cast<char>(c);
                }
            }
        }

        return is;
    };
    auto isSpace = [](const char& x)
    {
        return (((x) == ' ') || ((x) == '\t'));
    };
    auto isDigit = [](const char& x)->bool
    {
        return (static_cast<unsigned int>((x)-'0') < static_cast<unsigned int>(10));
    };
    auto tryParseDouble = [&isDigit](const char *s, const char *s_end, double *result)
    {
        if (s >= s_end) 
        {
            return false;
        }

        double mantissa = 0.0;
        // This exponent is base 2 rather than 10.
        // However the exponent we parse is supposed to be one of ten,
        // thus we must take care to convert the exponent/and or the
        // mantissa to a * 2^E, where a is the mantissa and E is the
        // exponent.
        // To get the final double we will use ldexp, it requires the
        // exponent to be in base 2.
        int exponent = 0;

        // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
        // TO JUMP OVER DEFINITIONS.
        char sign = '+';
        char exp_sign = '+';
        char const *curr = s;

        // How many characters were read in a loop.
        int read = 0;
        // Tells whether a loop terminated due to reaching s_end.
        bool end_not_reached = false;
        bool leading_decimal_dots = false;

        /*
                BEGIN PARSING.
        */

        // Find out what sign we've got.
        if (*curr == '+' || *curr == '-') {
            sign = *curr;
            curr++;
            if ((curr != s_end) && (*curr == '.')) {
                // accept. Somethig like `.7e+2`, `-.5234`
                leading_decimal_dots = true;
            }
        }
        else if (isDigit(*curr)) { /* Pass through. */
        }
        else if (*curr == '.') {
            // accept. Somethig like `.7e+2`, `-.5234`
            leading_decimal_dots = true;
        }
        else {
            goto fail;
        }

        // Read the integer part.
        end_not_reached = (curr != s_end);
        if (!leading_decimal_dots) {
            while (end_not_reached && isDigit(*curr)) {
                mantissa *= 10;
                mantissa += static_cast<int>(*curr - 0x30);
                curr++;
                read++;
                end_not_reached = (curr != s_end);
            }

            // We must make sure we actually got something.
            if (read == 0) goto fail;
        }

        // We allow numbers of form "#", "###" etc.
        if (!end_not_reached) goto assemble;

        // Read the decimal part.
        if (*curr == '.') {
            curr++;
            read = 1;
            end_not_reached = (curr != s_end);
            while (end_not_reached && isDigit(*curr)) {
                static const double pow_lut[] = {
                    1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001,
                };
                const int lut_entries = sizeof pow_lut / sizeof pow_lut[0];

                // NOTE: Don't use powf here, it will absolutely murder precision.
                mantissa += static_cast<int>(*curr - 0x30) *
                    (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read));
                read++;
                curr++;
                end_not_reached = (curr != s_end);
            }
        }
        else if (*curr == 'e' || *curr == 'E') {
        }
        else {
            goto assemble;
        }

        if (!end_not_reached) goto assemble;

        // Read the exponent part.
        if (*curr == 'e' || *curr == 'E') {
            curr++;
            // Figure out if a sign is present and if it is.
            end_not_reached = (curr != s_end);
            if (end_not_reached && (*curr == '+' || *curr == '-')) {
                exp_sign = *curr;
                curr++;
            }
            else if (isDigit(*curr)) { /* Pass through. */
            }
            else {
                // Empty E is not allowed.
                goto fail;
            }

            read = 0;
            end_not_reached = (curr != s_end);
            while (end_not_reached && isDigit(*curr)) {
                if (exponent > std::numeric_limits<int>::max() / 10) {
                    // Integer overflow
                    goto fail;
                }
                exponent *= 10;
                exponent += static_cast<int>(*curr - 0x30);
                curr++;
                read++;
                end_not_reached = (curr != s_end);
            }
            exponent *= (exp_sign == '+' ? 1 : -1);
            if (read == 0) goto fail;
        }

    assemble:
        *result = (sign == '+' ? 1 : -1) *
            (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
                : mantissa);
        return true;
    fail:
        return false;
    };
    auto parseReal = [&tryParseDouble](const char **token, double default_value = 0.0)->double
    {
        (*token) += strspn((*token), " \t");
        const char *end = (*token) + strcspn((*token), " \t\r");
        double val = default_value;
        tryParseDouble((*token), end, &val);
        double f = static_cast<double>(val);
        (*token) = end;
        return f;
    };

    BoundingBox box;

    std::string linebuf;
    while (in.peek() != -1)
    {
        safeGetline(in, linebuf);

        // Trim newline '\r\n' or '\n'
        if (linebuf.size() > 0)
        {
            if (linebuf[linebuf.size() - 1] == '\n')
                linebuf.erase(linebuf.size() - 1);
        }
        if (linebuf.size() > 0)
        {
            if (linebuf[linebuf.size() - 1] == '\r')
                linebuf.erase(linebuf.size() - 1);
        }

        // Skip if empty line.
        if (linebuf.empty())
        {
            continue;
        }

        // Skip leading space.
        const char *token = linebuf.c_str();
        token += strspn(token, " \t");

        if (token[0] == 'v' && isSpace(token[1]))
        {
            token += 2;

            double x = parseReal(&token);
            double y = parseReal(&token);
            double z = parseReal(&token);

            box.expandBy(x, y, z);
        }
    }

    return box;
}