C++程序  |  210行  |  7.28 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "ConfigManager.h"

#include "json/json.h"

#include <fstream>
#include <math.h>
#include <assert.h>


static const float kDegreesToRadians = M_PI / 180.0f;


static float normalizeToPlusMinus180degrees(float theta) {
    const float wraps = floor((theta+180.0f) / 360.0f);
    return theta - wraps*360.0f;
}


static bool readChildNodeAsFloat(const char* groupName,
                                 const Json::Value& parentNode,
                                 const char* childName,
                                 float* value) {
    // Must have a place to put the value!
    assert(value);

    Json::Value childNode = parentNode[childName];
    if (!childNode.isNumeric()) {
        printf("Missing or invalid field %s in record %s", childName, groupName);
        return false;
    }

    *value = childNode.asFloat();
    return true;
}


static bool ReadChildNodeAsUint(const char* groupName,
                                const Json::Value& parentNode,
                                const char* childName,
                                unsigned* value) {
    // Must have a place to put the value!
    assert(value);

    Json::Value childNode = parentNode[childName];
    if (!childNode.isNumeric()) {
        printf("Missing or invalid field %s in record %s", childName, groupName);
        return false;
    }

    *value = childNode.asUInt();
    return true;
}


bool ConfigManager::initialize(const char* configFileName)
{
    bool complete = true;

    // Set up a stream to read in the input file
    std::ifstream configStream(configFileName);

    // Parse the stream into JSON objects
    Json::Reader reader;
    Json::Value rootNode;
    bool parseOk = reader.parse(configStream, rootNode, false /* don't need comments */);
    if (!parseOk) {
        printf("Failed to read configuration file %s\n", configFileName);
        printf("%s\n", reader.getFormatedErrorMessages().c_str());
        return false;
    }


    //
    // Read car information
    //
    {
        Json::Value car = rootNode["car"];
        if (!car.isObject()) {
            printf("Invalid configuration format -- we expect a car description\n");
            return false;
        }
        complete &= readChildNodeAsFloat("car", car, "width",       &mCarWidth);
        complete &= readChildNodeAsFloat("car", car, "wheelBase",   &mWheelBase);
        complete &= readChildNodeAsFloat("car", car, "frontExtent", &mFrontExtent);
        complete &= readChildNodeAsFloat("car", car, "rearExtent",  &mRearExtent);
    }


    //
    // Read display layout information
    //
    {
        Json::Value displayNode = rootNode["display"];
        if (!displayNode.isObject()) {
            printf("Invalid configuration format -- we expect a display description\n");
            return false;
        }
        complete &= ReadChildNodeAsUint("display", displayNode, "width",      &mPixelWidth);
        complete &= ReadChildNodeAsUint("display", displayNode, "height",     &mPixelHeight);
        complete &= readChildNodeAsFloat("display", displayNode, "frontRange", &mFrontRangeInCarSpace);
        complete &= readChildNodeAsFloat("display", displayNode, "rearRange",  &mRearRangeInCarSpace);
    }


    //
    // Car top view texture properties for top down view
    //
    {
        Json::Value graphicNode = rootNode["graphic"];
        if (!graphicNode.isObject()) {
            printf("Invalid configuration format -- we expect a graphic description\n");
            return false;
        }
        complete &= readChildNodeAsFloat("graphic", graphicNode, "frontPixel", &mCarGraphicFrontPixel);
        complete &= readChildNodeAsFloat("display", graphicNode, "rearPixel",  &mCarGraphicRearPixel);
    }


    //
    // Read camera information
    // NOTE:  Missing positions and angles are not reported, but instead default to zero
    //
    {
        Json::Value cameraArray = rootNode["cameras"];
        if (!cameraArray.isArray()) {
            printf("Invalid configuration format -- we expect an array of cameras\n");
            return false;
        }

        mCameras.reserve(cameraArray.size());
        for (auto&& node: cameraArray) {
            // Get data from the configuration file
            Json::Value nameNode = node.get("cameraId", "MISSING");
            const char *cameraId = nameNode.asCString();
            printf("Loading camera %s\n", cameraId);

            Json::Value usageNode = node.get("function", "");
            const char *function = usageNode.asCString();

            float yaw   = node.get("yaw", 0).asFloat();
            float pitch = node.get("pitch", 0).asFloat();
            float hfov  = node.get("hfov", 0).asFloat();
            float vfov  = node.get("vfov", 0).asFloat();

            // Wrap the direction angles to be in the 180deg to -180deg range
            // Rotate 180 in yaw if necessary to flip the pitch into the +/-90degree range
            pitch = normalizeToPlusMinus180degrees(pitch);
            if (pitch > 90.0f) {
                yaw += 180.0f;
                pitch = 180.0f - pitch;
            }
            if (pitch < -90.0f) {
                yaw += 180.0f;
                pitch = -180.0f + pitch;
            }
            yaw = normalizeToPlusMinus180degrees(yaw);

            // Range check the FOV values to ensure they are postive and less than 180degrees
            if (hfov > 179.0f) {
                printf("Pathological horizontal field of view %f clamped to 179 degrees\n", hfov);
                hfov = 179.0f;
            }
            if (hfov < 1.0f) {
                printf("Pathological horizontal field of view %f clamped to 1 degree\n", hfov);
                hfov = 1.0f;
            }
            if (vfov > 179.0f) {
                printf("Pathological horizontal field of view %f clamped to 179 degrees\n", vfov);
                vfov = 179.0f;
            }
            if (vfov < 1.0f) {
                printf("Pathological horizontal field of view %f clamped to 1 degree\n", vfov);
                vfov = 1.0f;
            }

            // Store the camera info (converting degrees to radians in the process)
            CameraInfo info;
            info.position[0] = node.get("x", 0).asFloat();
            info.position[1] = node.get("y", 0).asFloat();
            info.position[2] = node.get("z", 0).asFloat();
            info.yaw         = yaw   * kDegreesToRadians;
            info.pitch       = pitch * kDegreesToRadians;
            info.hfov        = hfov  * kDegreesToRadians;
            info.vfov        = vfov  * kDegreesToRadians;
            info.cameraId    = cameraId;
            info.function    = function;

            mCameras.push_back(info);
        }
    }

    // If we got this far, we were successful as long as we found all our child fields
    return complete;
}