<commit_before>/*
 * Copyright 2015 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>

#include "Pid.h"
#include "SetPoint.h"
#include <cstdio>
#include <math.h>
#include "TempSensorMock.h"
#include "Actuator.h"
#include "ActuatorPwm.h"
#include "ActuatorTimeLimited.h"
#include "ActuatorSetPoint.h"
#include "runner.h"
#include <iostream>
#include <fstream>

struct StaticSetup{
public:
    StaticSetup(){
        BOOST_TEST_MESSAGE( "setup PID test fixture" );

        beerSensor = new TempSensorMock(20.0);
        fridgeSensor = new TempSensorMock(20.0);

        heaterPin = new ActuatorBool();
        heater = new ActuatorPwm(heaterPin, 4); // period 4s

        coolerPin = new ActuatorBool();
        coolerTimeLimited = new ActuatorTimeLimited(coolerPin, 120, 180); // 2 min minOn time, 3 min minOff
        cooler = new ActuatorPwm(coolerTimeLimited, 600); // period 10 min

        beerSet = new SetPointSimple(20.0);
        fridgeSet = new SetPointSimple(20.0);

        heaterPid = new Pid();
        coolerPid = new Pid();
        beerToFridgePid = new Pid();

        fridgeSetPointActuator = new ActuatorSetPoint(fridgeSet, beerSet);

        heaterPid->setOutputActuator(heater);
        coolerPid->setOutputActuator(cooler);
        coolerPid->setActuatorIsNegative(true);

    }
    ~StaticSetup(){
        BOOST_TEST_MESSAGE( "tear down PID test fixture" );
        delete beerSensor;
        delete fridgeSensor;

        delete coolerPin;
        delete coolerTimeLimited;
        delete heaterPin;
        delete cooler;
        delete heater;

        delete heaterPid;
        delete coolerPid;
        delete beerToFridgePid;

        delete beerSet;
        delete fridgeSet;
    }

    TempSensorMock * beerSensor;
    TempSensorMock * fridgeSensor;

    ActuatorDigital * coolerPin;
    ActuatorDigital * coolerTimeLimited;
    ActuatorDigital * heaterPin;
    ActuatorRange * cooler;
    ActuatorRange * heater;
    ActuatorRange * fridgeSetPointActuator;

    Pid * heaterPid;
    Pid * coolerPid;
    Pid * beerToFridgePid;

    SetPoint * beerSet;
    SetPoint * fridgeSet;
};

/* This class simulates a fridge is a simple way:
 * There are 3 heat capacities: the beer itself, the air in the fridge and the fridge walls.
 * The heater heats the air in the fridge directly.
 * The cooler cools the fridge walls, which in turn cool the fridge air.
 * This causes an extra delay when cooling and a potential source of overshoot
 */

struct Simulation{
    Simulation(){
        beerTemp = 20.0;
        airTemp = 20.0;
        wallTemp = 20.0;
        envTemp = 18.0;

        beerCapacity = 4.2 * 1.0 * 20; // heat capacity water * density of water * 20L volume (in kJ per kelvin).
        airCapacity = 5 * 1.0035 * 1.225 * 0.200; // 5 * heat capacity of dry air * density of air * 200L volume (in kJ per kelvin).
        // Moist air is much more complex, should calculate using enthalpy. Now just multiplied by 5.
        wallCapacity = 5.0; // just a guess

        heaterPower = 0.3; // 300W, in kW.
        coolerPower = 0.3; // 300W, in kW.

        airBeerTransfer= 0.02;
        wallAirTransfer= 0.04;
        envWallTransfer = 0.01;

    }
    virtual ~Simulation(){}

    void update(temp_t heaterValue, temp_t coolerValue){
        double beerTempNew = beerTemp;
        double airTempNew = airTemp;
        double wallTempNew = wallTemp;

        beerTempNew += (airTemp - beerTemp) * airBeerTransfer / beerCapacity;

        airTempNew += heaterPower * double(heaterValue) / (100.0 * airCapacity);
        airTempNew += (wallTemp - airTemp) * wallAirTransfer / airCapacity;
        airTempNew += (beerTemp - airTemp) * airBeerTransfer / airCapacity;

        wallTempNew -= coolerPower * double(coolerValue) / (100.0 * wallCapacity);
        wallTempNew += (envTemp - wallTemp) * envWallTransfer / wallCapacity;
        wallTempNew += (airTemp - wallTemp) * wallAirTransfer/ wallCapacity;

        airTemp = airTempNew;
        beerTemp = beerTempNew;
        wallTemp = wallTempNew;
    }

    double beerTemp;
    double airTemp;
    double wallTemp;
    double envTemp;

    double beerCapacity;
    double airCapacity;
    double wallCapacity;

    double heaterPower;
    double coolerPower;

    double airBeerTransfer;
    double wallAirTransfer;
    double envWallTransfer;
};

/* Below are a few static setups that show how control can be set up.
 * The first 4 are simple: a single actuator, acting on beer or fridge temperature
 */

// Just a heater, acting on beer temperature directly
struct SimBeerHeater : public StaticSetup {
    Simulation sim;
    SimBeerHeater(){
        heaterPid->setInputSensor(beerSensor);
        heaterPid->setSetPoint(beerSet);
        heaterPid->setInputFilter(0);
        heaterPid->setDerivativeFilter(4);
        heaterPid->setConstants(100.0, 5.0, -100.0);
    }

    void update(){
        sim.update(heater->getValue(), cooler->getValue());
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        heaterPid->update();
    }
};

// Just a heater, acting on fridge temperature directly
struct SimFridgeHeater : public StaticSetup {
    Simulation sim;
    SimFridgeHeater(){
        heaterPid->setInputSensor(fridgeSensor);
        heaterPid->setSetPoint(fridgeSet);
        heaterPid->setInputFilter(0);
        heaterPid->setDerivativeFilter(2);
        heaterPid->setConstants(20.0, 10.0, -3.0);
    }

    void update(){
        sim.update(heater->getValue(), cooler->getValue());
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        heaterPid->update();
    }
};

// Just a cooler, acting on beer temperature directly
struct SimBeerCooler : public StaticSetup {
    Simulation sim;
    SimBeerCooler(){
        coolerPid->setInputSensor(beerSensor);
        coolerPid->setSetPoint(beerSet);
        coolerPid->setInputFilter(0);
        coolerPid->setDerivativeFilter(4);
        coolerPid->setConstants(100.0, 5.0, -100.0);

    }

    void update(){
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        coolerPid->update();
        cooler->update();
        // because cooler is time limited, get actual output from pin
        if(coolerPin->isActive()){
            sim.update(heater->getValue(), 100.0);
        } else{
            sim.update(heater->getValue(), 0.0);
        }
        delay(1000); // simulate actual time passing for pin state of cooler, which is time limited
    }
};

// Just a cooler, acting on fridge temperature directly
struct SimFridgeCooler : public StaticSetup {
    Simulation sim;
    SimFridgeCooler(){
        coolerPid->setInputSensor(fridgeSensor);
        coolerPid->setSetPoint(fridgeSet);
        coolerPid->setInputFilter(0);
        coolerPid->setDerivativeFilter(2);
        coolerPid->setConstants(20.0, 5.0, -5.0);
    }

    void update(){
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        coolerPid->update();
        cooler->update();

        // because cooler is time limited, get actual output from pin
        if(coolerPin->isActive()){
            sim.update(heater->getValue(), 100.0);
        } else{
            sim.update(heater->getValue(), 0.0);
        }
        delay(1000); // simulate actual time passing for pin state of cooler, which is time limited
    }
};

BOOST_AUTO_TEST_SUITE( simulation_test)

// Test heating fridge air based on beer temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Heater_Acts_On_Beer, SimBeerHeater)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, heater pwm, p, i, d" << endl;
    double SetPointDouble = 19;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 21;
        }
        if(t==2500){
            SetPointDouble = 24;
        }
        beerSet->write(SetPointDouble);
        update();

        csv     << beerSet->read() << "," // setpoint
                << heaterPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << heater->getValue() << "," // actuator output
                << heaterPid->p << "," // proportional action
                << heaterPid->i << "," // integral action
                << heaterPid->d // derivative action
                << endl;
    }
    csv.close();
}

// Test heating fridge air based on fridge air temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Heater_Acts_On_Fridge_Air, SimFridgeHeater)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, heater pwm, p, i, d" << endl;
    double SetPointDouble = 19;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 24;
        }
        if(t==2500){
            SetPointDouble = 28;
        }
        fridgeSet->write(SetPointDouble);
        update();

        csv     << fridgeSet->read() << "," // setpoint
                << heaterPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << heater->getValue() << "," // actuator output
                << heaterPid->p << "," // proportional action
                << heaterPid->i << "," // integral action
                << heaterPid->d // derivative action
                << endl;
    }
    csv.close();
}

// Test cooling fridge air (via wall) based on beer temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Cooler_Acts_On_Beer, SimBeerCooler)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, cooler pwm, p, i, d, cooler pin" << endl;
    double SetPointDouble = 21;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 19;
        }
        if(t==2500){
            SetPointDouble = 18.5;
        }
        beerSet->write(SetPointDouble);
        update();

        csv     << beerSet->read() << "," // setpoint
                << coolerPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << cooler->getValue() << "," // actuator output
                << coolerPid->p << "," // proportional action
                << coolerPid->i << "," // integral action
                << coolerPid->d << "," // derivative action
                << coolerPin->isActive() // actual cooler pin state
                << endl;
    }
    csv.close();
}

// Test cooling fridge air (via wall) based on fridge air temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Cooler_Acts_On_Fridge_Air, SimFridgeCooler)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, cooler pwm, p, i, d, cooler pin" << endl;
    double SetPointDouble = 21;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 19;
        }
        if(t==2500){
            SetPointDouble = 15;
        }
        fridgeSet->write(SetPointDouble);
        update();

        csv     << fridgeSet->read() << "," // setpoint
                << coolerPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << cooler->getValue() << "," // actuator output
                << coolerPid->p << "," // proportional action
                << coolerPid->i << "," // integral action
                << coolerPid->d << "," // derivative action
                << coolerPin->isActive() // actual cooler pin state
                << endl;
    }
    csv.close();
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>WIP testing with cooler<commit_after>/*
 * Copyright 2015 BrewPi/Elco Jacobs.
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>

#include "Pid.h"
#include "SetPoint.h"
#include <cstdio>
#include <math.h>
#include "TempSensorMock.h"
#include "Actuator.h"
#include "ActuatorPwm.h"
#include "ActuatorTimeLimited.h"
#include "ActuatorSetPoint.h"
#include "runner.h"
#include <iostream>
#include <fstream>

struct StaticSetup{
public:
    StaticSetup(){
        BOOST_TEST_MESSAGE( "setup PID test fixture" );

        beerSensor = new TempSensorMock(20.0);
        fridgeSensor = new TempSensorMock(20.0);

        heaterPin = new ActuatorBool();
        heater = new ActuatorPwm(heaterPin, 4); // period 4s

        coolerPin = new ActuatorBool();
        coolerTimeLimited = new ActuatorTimeLimited(coolerPin, 120, 180); // 2 min minOn time, 3 min minOff
        cooler = new ActuatorPwm(coolerTimeLimited, 600); // period 10 min

        beerSet = new SetPointSimple(20.0);
        fridgeSet = new SetPointSimple(20.0);

        heaterPid = new Pid();
        coolerPid = new Pid();
        beerToFridgePid = new Pid();

        fridgeSetPointActuator = new ActuatorSetPoint(fridgeSet, beerSet);

        heaterPid->setOutputActuator(heater);
        coolerPid->setOutputActuator(cooler);
        coolerPid->setActuatorIsNegative(true);

    }
    ~StaticSetup(){
        BOOST_TEST_MESSAGE( "tear down PID test fixture" );
        delete beerSensor;
        delete fridgeSensor;

        delete coolerPin;
        delete coolerTimeLimited;
        delete heaterPin;
        delete cooler;
        delete heater;

        delete heaterPid;
        delete coolerPid;
        delete beerToFridgePid;

        delete beerSet;
        delete fridgeSet;
    }

    TempSensorMock * beerSensor;
    TempSensorMock * fridgeSensor;

    ActuatorDigital * coolerPin;
    ActuatorDigital * coolerTimeLimited;
    ActuatorDigital * heaterPin;
    ActuatorRange * cooler;
    ActuatorRange * heater;
    ActuatorRange * fridgeSetPointActuator;

    Pid * heaterPid;
    Pid * coolerPid;
    Pid * beerToFridgePid;

    SetPoint * beerSet;
    SetPoint * fridgeSet;
};

/* This class simulates a fridge is a simple way:
 * There are 3 heat capacities: the beer itself, the air in the fridge and the fridge walls.
 * The heater heats the air in the fridge directly.
 * The cooler cools the fridge walls, which in turn cool the fridge air.
 * This causes an extra delay when cooling and a potential source of overshoot
 */

struct Simulation{
    Simulation(){
        beerTemp = 20.0;
        airTemp = 20.0;
        wallTemp = 20.0;
        envTemp = 18.0;

        beerCapacity = 4.2 * 1.0 * 20; // heat capacity water * density of water * 20L volume (in kJ per kelvin).
        airCapacity = 5 * 1.0035 * 1.225 * 0.200; // 5 * heat capacity of dry air * density of air * 200L volume (in kJ per kelvin).
        // Moist air is much more complex, should calculate using enthalpy. Now just multiplied by 5.
        wallCapacity = 5.0; // just a guess

        heaterPower = 0.3; // 300W, in kW.
        coolerPower = 0.3; // 300W, in kW.

        airBeerTransfer= 0.02;
        wallAirTransfer= 0.04;
        envWallTransfer = 0.01;

    }
    virtual ~Simulation(){}

    void update(temp_t heaterValue, temp_t coolerValue){
        double beerTempNew = beerTemp;
        double airTempNew = airTemp;
        double wallTempNew = wallTemp;

        beerTempNew += (airTemp - beerTemp) * airBeerTransfer / beerCapacity;

        airTempNew += heaterPower * double(heaterValue) / (100.0 * airCapacity);
        airTempNew += (wallTemp - airTemp) * wallAirTransfer / airCapacity;
        airTempNew += (beerTemp - airTemp) * airBeerTransfer / airCapacity;

        wallTempNew -= coolerPower * double(coolerValue) / (100.0 * wallCapacity);
        wallTempNew += (envTemp - wallTemp) * envWallTransfer / wallCapacity;
        wallTempNew += (airTemp - wallTemp) * wallAirTransfer/ wallCapacity;

        airTemp = airTempNew;
        beerTemp = beerTempNew;
        wallTemp = wallTempNew;
    }

    double beerTemp;
    double airTemp;
    double wallTemp;
    double envTemp;

    double beerCapacity;
    double airCapacity;
    double wallCapacity;

    double heaterPower;
    double coolerPower;

    double airBeerTransfer;
    double wallAirTransfer;
    double envWallTransfer;
};

/* Below are a few static setups that show how control can be set up.
 * The first 4 are simple: a single actuator, acting on beer or fridge temperature
 */

// Just a heater, acting on beer temperature directly
struct SimBeerHeater : public StaticSetup {
    Simulation sim;
    SimBeerHeater(){
        heaterPid->setInputSensor(beerSensor);
        heaterPid->setSetPoint(beerSet);
        heaterPid->setInputFilter(0);
        heaterPid->setDerivativeFilter(4);
        heaterPid->setConstants(100.0, 5.0, -100.0);
    }

    void update(){
        sim.update(heater->getValue(), cooler->getValue());
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        heaterPid->update();
    }
};

// Just a heater, acting on fridge temperature directly
struct SimFridgeHeater : public StaticSetup {
    Simulation sim;
    SimFridgeHeater(){
        heaterPid->setInputSensor(fridgeSensor);
        heaterPid->setSetPoint(fridgeSet);
        heaterPid->setInputFilter(0);
        heaterPid->setDerivativeFilter(2);
        heaterPid->setConstants(20.0, 10.0, -3.0);
    }

    void update(){
        sim.update(heater->getValue(), cooler->getValue());
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        heaterPid->update();
    }
};

// Just a cooler, acting on beer temperature directly
struct SimBeerCooler : public StaticSetup {
    Simulation sim;
    SimBeerCooler(){
        coolerPid->setInputSensor(beerSensor);
        coolerPid->setSetPoint(beerSet);
        coolerPid->setInputFilter(0);
        coolerPid->setDerivativeFilter(4);
        coolerPid->setConstants(100.0, 0.0, -100.0);

    }

    void update(){
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        coolerPid->update();
        cooler->update();
        // because cooler is time limited, get actual output from pin
        if(coolerPin->isActive()){
            sim.update(heater->getValue(), 100.0);
        } else{
            sim.update(heater->getValue(), 0.0);
        }
        delay(1000); // simulate actual time passing for pin state of cooler, which is time limited
    }
};

// Just a cooler, acting on fridge temperature directly
struct SimFridgeCooler : public StaticSetup {
    Simulation sim;
    SimFridgeCooler(){
        coolerPid->setInputSensor(fridgeSensor);
        coolerPid->setSetPoint(fridgeSet);
        coolerPid->setInputFilter(0);
        coolerPid->setDerivativeFilter(2);
        coolerPid->setConstants(20.0, 5.0, -5.0);
    }

    void update(){
        beerSensor->setTemp(sim.beerTemp);
        fridgeSensor->setTemp(sim.airTemp);
        coolerPid->update();
        cooler->update();

        // because cooler is time limited, get actual output from pin
        if(coolerPin->isActive()){
            sim.update(heater->getValue(), 100.0);
        } else{
            sim.update(heater->getValue(), 0.0);
        }
        delay(1000); // simulate actual time passing for pin state of cooler, which is time limited
    }
};

BOOST_AUTO_TEST_SUITE( simulation_test)

// Test heating fridge air based on beer temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Heater_Acts_On_Beer, SimBeerHeater)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, heater pwm, p, i, d" << endl;
    double SetPointDouble = 19;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 21;
        }
        if(t==2500){
            SetPointDouble = 24;
        }
        beerSet->write(SetPointDouble);
        update();

        csv     << beerSet->read() << "," // setpoint
                << heaterPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << heater->getValue() << "," // actuator output
                << heaterPid->p << "," // proportional action
                << heaterPid->i << "," // integral action
                << heaterPid->d // derivative action
                << endl;
    }
    csv.close();
}

// Test heating fridge air based on fridge air temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Heater_Acts_On_Fridge_Air, SimFridgeHeater)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, heater pwm, p, i, d" << endl;
    double SetPointDouble = 19;
    for(int t = 0; t < 6000; t++){
        if(t==500){
            SetPointDouble = 24;
        }
        if(t==2500){
            SetPointDouble = 28;
        }
        fridgeSet->write(SetPointDouble);
        update();

        csv     << fridgeSet->read() << "," // setpoint
                << heaterPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << heater->getValue() << "," // actuator output
                << heaterPid->p << "," // proportional action
                << heaterPid->i << "," // integral action
                << heaterPid->d // derivative action
                << endl;
    }
    csv.close();
}

// Test cooling fridge air (via wall) based on beer temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Cooler_Acts_On_Beer, SimBeerCooler)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, cooler pwm, p, i, d, cooler pin" << endl;
    double SetPointDouble = 21;
    for(int t = 0; t < 10000; t++){
        if(t==1000){
            SetPointDouble = 19;
        }
        if(t==4000){
            SetPointDouble = 18.5;
        }
        beerSet->write(SetPointDouble);
        update();
        if(t==7000){
            update();
        }

        csv     << beerSet->read() << "," // setpoint
                << coolerPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << cooler->getValue() << "," // actuator output
                << coolerPid->p << "," // proportional action
                << coolerPid->i << "," // integral action
                << coolerPid->d << "," // derivative action
                << coolerPin->isActive() // actual cooler pin state
                << endl;
    }
    csv.close();
}

// Test cooling fridge air (via wall) based on fridge air temperature (non-cascaded control)
BOOST_FIXTURE_TEST_CASE(Simulate_Air_Cooler_Acts_On_Fridge_Air, SimFridgeCooler)
{
    ofstream csv("./test_results/" + boost_test_name() + ".csv");
    csv << "setPoint, error, beer sensor, fridge air sensor, fridge wall temp, cooler pwm, p, i, d, cooler pin" << endl;
    double SetPointDouble = 21;
    for(int t = 0; t < 10000; t++){
        if(t==1000){
            SetPointDouble = 19;
        }
        if(t==4000){
            SetPointDouble = 15;
        }
        fridgeSet->write(SetPointDouble);
        update();

        csv     << fridgeSet->read() << "," // setpoint
                << coolerPid->inputError << "," //error
                << beerSensor->read() << "," // beer temp
                << fridgeSensor->read() << "," // air temp
                << sim.wallTemp << "," // fridge wall temperature
                << cooler->getValue() << "," // actuator output
                << coolerPid->p << "," // proportional action
                << coolerPid->i << "," // integral action
                << coolerPid->d << "," // derivative action
                << coolerPin->isActive() // actual cooler pin state
                << endl;
    }
    csv.close();
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "Environment.h"
#include "Logger.h"
#include "SceneLoadContext.h"
#include "shader/INodePlugin.h"

#include <SeExpr2/ExprFunc.h>
#include <SeExpr2/Expression.h>
#include <SeExpr2/Vec.h>

#include <fstream>

namespace PR {
constexpr char POSITION_VARIABLE[]	 = "p";
constexpr char WAVELENGTH_VARIABLE[] = "w";
constexpr char TEXTURE_VARIABLE[]	 = "uv";

struct ScalarVariable : public SeExpr2::ExprVarRef {
	ScalarVariable(bool isConst)
		: SeExpr2::ExprVarRef(isConst ? SeExpr2::ExprType().FP(1).Constant()
									  : SeExpr2::ExprType().FP(1).Varying())
		, Value{ 0 }
	{
	}

	float Value;

	void eval(double* result) { result[0] = Value; }
	void eval(const char**) { PR_ASSERT(false, "eval on string not implemented!"); }
};

struct SpectralVariable : public SeExpr2::ExprVarRef {
	SpectralVariable(bool isConst)
		: SeExpr2::ExprVarRef(isConst ? SeExpr2::ExprType().FP(1).Constant()
									  : SeExpr2::ExprType().FP(1).Varying())
		, Value(SpectralBlob::Zero())
		, CurrentEntry(0)
	{
	}

	SpectralBlob Value;
	int CurrentEntry;

	void eval(double* result) { result[0] = Value(CurrentEntry); }
	void eval(const char**) { PR_ASSERT(false, "eval on string not implemented!"); }
};

struct VectorVariable : public SeExpr2::ExprVarRef {
	VectorVariable(int d, bool isConst)
		: SeExpr2::ExprVarRef(isConst ? SeExpr2::ExprType().FP(d).Constant()
									  : SeExpr2::ExprType().FP(d).Varying())
		, Value{ 0, 0, 0 }
		, Dim(d)
	{
		PR_ASSERT(d > 0 && d <= 3, "Expected valid dimension");
	}

	float Value[3];
	const int Dim;

	void eval(double* result)
	{
		for (int i = 0; i < Dim; ++i)
			result[i] = Value[i];
	}

	void eval(const char**) { PR_ASSERT(false, "eval on string not implemented!"); }
};

class PrExpression : public SeExpr2::Expression {
public:
	PrExpression(const std::string& expr, bool isVec)
		: SeExpr2::Expression(expr, SeExpr2::ExprType().FP(isVec ? 3 : 1))
	{
	}

	PrExpression(PrExpression&& other) = default;

	std::unordered_map<ScalarVariable*, std::shared_ptr<FloatScalarNode>> ScalarNodes;
	std::unordered_map<SpectralVariable*, std::shared_ptr<FloatSpectralNode>> SpectralNodes;
	std::unordered_map<VectorVariable*, std::shared_ptr<FloatVectorNode>> VectorNodes;

	mutable std::unordered_map<std::string, std::unique_ptr<ScalarVariable>> ScalarVariableReferences;
	mutable std::unordered_map<std::string, std::unique_ptr<SpectralVariable>> SpectralVariableReferences;
	mutable std::unordered_map<std::string, std::unique_ptr<VectorVariable>> VectorVariableReferences;

	mutable std::unique_ptr<VectorVariable> PositionVariable;
	mutable std::unique_ptr<VectorVariable> TextureVariable;
	mutable std::unique_ptr<SpectralVariable> WavelengthVariable;

	SeExpr2::ExprVarRef* resolveVar(const std::string& name) const
	{
		if (POSITION_VARIABLE == name)
			return PositionVariable.get();
		else if (TEXTURE_VARIABLE == name)
			return TextureVariable.get();
		else if (WAVELENGTH_VARIABLE == name)
			return WavelengthVariable.get();
		else {
			const auto i1 = ScalarVariableReferences.find(name);
			if (i1 != ScalarVariableReferences.end())
				return i1->second.get();

			const auto i2 = SpectralVariableReferences.find(name);
			if (i2 != SpectralVariableReferences.end())
				return i2->second.get();

			const auto i3 = VectorVariableReferences.find(name);
			if (i3 != VectorVariableReferences.end())
				return i3->second.get();
			return nullptr;
		}
	}
};

// No Spectrals
inline static void updateBasicNodes(const ShadingContext& ctx, const std::shared_ptr<PrExpression>& expr)
{
	for (auto entry : expr->ScalarNodes)
		entry.first->Value = entry.second->eval(ctx);

	for (auto entry : expr->VectorNodes) {
		auto vec			  = entry.second->eval(ctx);
		entry.first->Value[0] = vec(0);
		entry.first->Value[1] = vec(1);
		entry.first->Value[2] = vec(2);
	}

	if (expr->TextureVariable) {
		expr->TextureVariable->Value[0] = ctx.UV[0];
		expr->TextureVariable->Value[1] = ctx.UV[1];
	}
}

class ScalarExpressionNode : public FloatScalarNode {
public:
	explicit ScalarExpressionNode(const std::shared_ptr<PrExpression>& expr)
		: mExpr(expr)
	{
	}

	float eval(const ShadingContext& ctx) const override
	{
		// Update nodes
		updateBasicNodes(ctx, mExpr);

		const double* result = mExpr->evalFP();
		return result[0];
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Scalar Expression (" << mExpr->getExpr() << ")";
		return sstream.str();
	}

private:
	std::shared_ptr<PrExpression> mExpr;
};

class SpectralExpressionNode : public FloatSpectralNode {
public:
	explicit SpectralExpressionNode(const std::shared_ptr<PrExpression>& expr)
		: mExpr(expr)
	{
	}

	// TODO: Better way?
	SpectralBlob eval(const ShadingContext& ctx) const override
	{
		// Update nodes
		updateBasicNodes(ctx, mExpr);

		// Update spectral nodes
		for (auto entry : mExpr->SpectralNodes)
			entry.first->Value = entry.second->eval(ctx);

		if (mExpr->WavelengthVariable)
			mExpr->WavelengthVariable->Value = ctx.WavelengthNM;

		// Calculate each wavelength (not really the best solution)
		SpectralBlob output;
		for (size_t i = 0; i < PR_SPECTRAL_BLOB_SIZE; ++i) {
			for (auto entry : mExpr->SpectralNodes)
				entry.first->CurrentEntry = i;

			if (mExpr->WavelengthVariable)
				mExpr->WavelengthVariable->CurrentEntry = i;

			const double* result = mExpr->evalFP();
			output[i]			 = result[0];
		}
		return output;
	}

	// TODO: Get a better way to achieve this!
	Vector2i queryRecommendedSize() const override
	{
		return Vector2i(1, 1);
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Spectral Expression (" << mExpr->getExpr() << ")";
		return sstream.str();
	}

private:
	std::shared_ptr<PrExpression> mExpr;
};

class VectorExpressionNode : public FloatVectorNode {
public:
	explicit VectorExpressionNode(const std::shared_ptr<PrExpression>& expr)
		: mExpr(expr)
	{
	}

	Vector3f eval(const ShadingContext& ctx) const override
	{
		// Update nodes
		updateBasicNodes(ctx, mExpr);

		const double* result = mExpr->evalFP();
		return Vector3f(result[0], result[1], result[2]);
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Vector3f Expression (" << mExpr->getExpr() << ")";
		return sstream.str();
	}

private:
	std::shared_ptr<PrExpression> mExpr;
};

class ExpressionPlugin : public INodePlugin {
public:
	std::shared_ptr<INode> create(uint32, const std::string& type_name, const SceneLoadContext& ctx) override
	{
		std::string expr_str;
		if (type_name == "expr" || type_name == "vexpr") {
			expr_str = ctx.Parameters.getString("expression", "");
			if (expr_str.empty())
				expr_str = ctx.Parameters.getString(0, "");
		} else {
			std::string path = ctx.Parameters.getString("file", "");
			if (path.empty())
				path = ctx.Parameters.getString(0, "");

			std::ifstream stream(path, std::ios::in);
			if (!stream.good()) {
				PR_LOG(L_ERROR) << "[SeExpr2] Can not load file " << path << std::endl;
				return nullptr;
			}
			expr_str = std::string((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
		}

		const bool isVec = (type_name == "vexpr" || type_name == "fvexpr");
		auto expr		 = std::make_shared<PrExpression>(expr_str, isVec);

		// Setup user variables
		for (const auto& entry : ctx.Parameters.parameters()) {
			const auto& param = entry.second;

			switch (param.type()) {
			case PT_Invalid:
				continue;
			case PT_Bool: {
				std::unique_ptr<ScalarVariable> constant	= std::make_unique<ScalarVariable>(true);
				constant->Value								= param.getBool(false) ? 1.0f : 0.0f;
				expr->ScalarVariableReferences[entry.first] = std::move(constant);
			} break;
			case PT_Int:
			case PT_UInt:
			case PT_Number: {
				std::unique_ptr<ScalarVariable> constant	= std::make_unique<ScalarVariable>(true);
				constant->Value								= param.getNumber(0.0f);
				expr->ScalarVariableReferences[entry.first] = std::move(constant);
			} break;
			case PT_String:
			case PT_Reference: {
				auto node = ctx.Env->lookupRawNode(param);
				switch (node->type()) {
				case NT_FloatScalar: {
					auto var									= std::make_unique<ScalarVariable>(false);
					expr->ScalarNodes[var.get()]				= std::reinterpret_pointer_cast<FloatScalarNode>(node);
					expr->ScalarVariableReferences[entry.first] = std::move(var);
				} break;
				case NT_FloatSpectral: {
					auto var									  = std::make_unique<SpectralVariable>(false);
					expr->SpectralNodes[var.get()]				  = std::reinterpret_pointer_cast<FloatSpectralNode>(node);
					expr->SpectralVariableReferences[entry.first] = std::move(var);
				} break;
				case NT_FloatVector: {
					auto var									= std::make_unique<VectorVariable>(3, false);
					expr->VectorNodes[var.get()]				= std::reinterpret_pointer_cast<FloatVectorNode>(node);
					expr->VectorVariableReferences[entry.first] = std::move(var);
				} break;
				}
			} break;
			}
		}

		// Setup standard variables
		if (expr->usesVar(POSITION_VARIABLE))
			expr->PositionVariable = std::make_unique<VectorVariable>(3, false);
		if (expr->usesVar(TEXTURE_VARIABLE))
			expr->TextureVariable = std::make_unique<VectorVariable>(2, false);
		if (expr->usesVar(WAVELENGTH_VARIABLE))
			expr->WavelengthVariable = std::make_unique<SpectralVariable>(false);

		// const bool isSpatialVarying	 = expr->PositionVariable || expr->TextureVariable;
		const bool isSpectralVarying = (expr->WavelengthVariable != nullptr) || !expr->SpectralVariableReferences.empty();

		if (!expr->isValid()) {
			PR_LOG(L_ERROR) << "[SeExpr2] Parsing Error: " << expr->parseError() << std::endl;
			return nullptr;
		}

		if (isSpectralVarying && isVec) {
			PR_LOG(L_ERROR) << "[SeExpr2] A spectral varying expression can not be combined with a vector expression" << std::endl;
			return nullptr;
		}

		if (isVec) {
			return std::make_shared<VectorExpressionNode>(expr);
		} else if (isSpectralVarying) {
			return std::make_shared<SpectralExpressionNode>(expr);
		} else {
			return std::make_shared<ScalarExpressionNode>(expr);
		}
	}

	const std::vector<std::string>& getNames() const override
	{
		const static std::vector<std::string> names({ "expr", "vexpr", "fexpr", "fvexpr" });
		return names;
	}

	bool init() override
	{
		return true;
	}
};
} // namespace PR

PR_PLUGIN_INIT(PR::ExpressionPlugin, _PR_PLUGIN_NAME, PR_PLUGIN_VERSION)<commit_msg>Improved thread safety<commit_after>#include "Environment.h"
#include "Logger.h"
#include "SceneLoadContext.h"
#include "renderer/RenderContext.h"
#include "shader/INodePlugin.h"

#include <SeExpr2/ExprFunc.h>
#include <SeExpr2/Expression.h>
#include <SeExpr2/VarBlock.h>
#include <SeExpr2/Vec.h>

#include <fstream>
#include <regex>

namespace PR {
constexpr char POSITION_VARIABLE[]	 = "P";
constexpr char WAVELENGTH_VARIABLE[] = "w";
constexpr char TEXTURE_U_VARIABLE[]	 = "u";
constexpr char TEXTURE_V_VARIABLE[]	 = "v";

static inline bool usesVariable(const std::string& expr, const std::string& var)
{
	return std::regex_search(expr, std::regex("\\b" + var + "\\b"));
}

struct ExpressionContainer {
	SeExpr2::Expression Expr;
	SeExpr2::VarBlockCreator Creator;
	std::vector<SeExpr2::VarBlock> VarBlocks;

	std::vector<std::pair<int, std::shared_ptr<FloatScalarNode>>> ScalarNodes;
	std::vector<std::pair<int, std::pair<SpectralBlob, std::shared_ptr<FloatSpectralNode>>>> SpectralNodes;
	std::vector<std::pair<int, std::shared_ptr<FloatVectorNode>>> VectorNodes;
	std::vector<std::pair<int, float>> NumberConstants;
	std::vector<std::pair<int, Vector3f>> VectorConstants;

	int PositionVariable   = -1;
	int WavelengthVariable = -1;
	int TextureUVariable   = -1;
	int TextureVVariable   = -1;

	inline ExpressionContainer(const std::string& str, bool isVec)
		: Expr(str)
	{
		Expr.setDesiredReturnType(SeExpr2::TypeVec(isVec ? 3 : 1));
		Expr.setVarBlockCreator(&Creator);

		// Setup standard variables
		if (usesVariable(str, POSITION_VARIABLE))
			PositionVariable = Creator.registerVariable(POSITION_VARIABLE,
														SeExpr2::ExprType().FP(3).Varying());
		if (usesVariable(str, TEXTURE_U_VARIABLE))
			TextureUVariable = Creator.registerVariable(TEXTURE_U_VARIABLE,
														SeExpr2::ExprType().FP(1).Varying());
		if (usesVariable(str, TEXTURE_V_VARIABLE))
			TextureVVariable = Creator.registerVariable(TEXTURE_V_VARIABLE,
														SeExpr2::ExprType().FP(1).Varying());
		if (usesVariable(str, WAVELENGTH_VARIABLE))
			WavelengthVariable = Creator.registerVariable(WAVELENGTH_VARIABLE,
														  SeExpr2::ExprType().FP(1).Varying());
	}

	inline void updateConstants(SeExpr2::VarBlock* block) const
	{
		for (const auto& entry : NumberConstants)
			*block->Pointer(entry.first) = entry.second;

		for (const auto& entry : VectorConstants) {
			Vector3f var = entry.second;
			auto* ptr	 = block->Pointer(entry.first);
			ptr[0]		 = var(0);
			ptr[1]		 = var(1);
			ptr[2]		 = var(2);
		}
	}

	inline void setupThreadData(size_t thread_count)
	{
		for (size_t i = 0; i < thread_count; ++i) {
			SeExpr2::VarBlock block = Creator.create(true);
			updateConstants(&block);
			VarBlocks.push_back(std::move(block));
		}
	}

	inline void updateVaryings(const ShadingContext& ctx)
	{
		auto* varblock = &VarBlocks[ctx.ThreadIndex];

		for (const auto& entry : ScalarNodes)
			*varblock->Pointer(entry.first) = entry.second->eval(ctx);

		for (auto& entry : SpectralNodes)
			entry.second.first = entry.second.second->eval(ctx);

		for (const auto& entry : VectorNodes) {
			Vector3f var = entry.second->eval(ctx);
			auto* ptr	 = varblock->Pointer(entry.first);
			ptr[0]		 = var(0);
			ptr[1]		 = var(1);
			ptr[2]		 = var(2);
		}

		/*if (PositionVariable >= 0) {
			auto* ptr = VarBlock->Pointer(PositionVariable);
			ptr[0]	  = ctx.Position[0];
			ptr[1]	  = ctx.Position[1];
			ptr[2]	  = ctx.Position[2];
		}*/

		if (TextureUVariable >= 0)
			*varblock->Pointer(TextureUVariable) = ctx.UV(0);

		if (TextureVVariable >= 0)
			*varblock->Pointer(TextureVVariable) = ctx.UV(1);
	}

	inline void updateSpectralVaryings(size_t i, const ShadingContext& ctx)
	{
		auto* varblock = &VarBlocks[ctx.ThreadIndex];
		for (auto& entry : SpectralNodes)
			*varblock->Pointer(entry.first) = entry.second.first[i];

		if (WavelengthVariable >= 0)
			*varblock->Pointer(WavelengthVariable) = ctx.WavelengthNM[i];
	}

	inline void registerConstant(const std::string& name, float constant)
	{
		int handle = Creator.registerVariable(name, SeExpr2::ExprType().FP(1).Constant());
		NumberConstants.emplace_back(handle, constant);
	}

	inline void registerConstant(const std::string& name, const Vector3f& constant)
	{
		int handle = Creator.registerVariable(name, SeExpr2::ExprType().FP(3).Constant());
		VectorConstants.emplace_back(handle, constant);
	}

	inline void registerVarying(const std::string& name, const std::shared_ptr<FloatScalarNode>& node)
	{
		int handle = Creator.registerVariable(name, SeExpr2::ExprType().FP(1).Varying());
		ScalarNodes.emplace_back(handle, node);
	}

	inline void registerVarying(const std::string& name, const std::shared_ptr<FloatSpectralNode>& node)
	{
		int handle = Creator.registerVariable(name, SeExpr2::ExprType().FP(1).Varying());
		SpectralNodes.emplace_back(handle, std::make_pair(SpectralBlob(), node));
	}

	inline void registerVarying(const std::string& name, const std::shared_ptr<FloatVectorNode>& node)
	{
		int handle = Creator.registerVariable(name, SeExpr2::ExprType().FP(3).Varying());
		VectorNodes.emplace_back(handle, node);
	}

	inline bool isSpectralVarying() const
	{
		return WavelengthVariable >= 0 || !SpectralNodes.empty();
	}
};

class ScalarExpressionNode : public FloatScalarNode {
public:
	explicit ScalarExpressionNode(const std::shared_ptr<ExpressionContainer>& expr)
		: mExpr(expr)
	{
	}

	float eval(const ShadingContext& ctx) const override
	{
		mExpr->updateVaryings(ctx);
		const double* result = mExpr->Expr.evalFP(&mExpr->VarBlocks[ctx.ThreadIndex]);
		return result[0];
	}

	void beforeRender(RenderContext* ctx) override
	{
		FloatScalarNode::beforeRender(ctx);
		mExpr->setupThreadData(ctx->threads());
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Scalar Expression (" << mExpr->Expr.getExpr() << ")";
		return sstream.str();
	}

private:
	mutable std::shared_ptr<ExpressionContainer> mExpr;
};

class SpectralExpressionNode : public FloatSpectralNode {
public:
	explicit SpectralExpressionNode(const std::shared_ptr<ExpressionContainer>& expr)
		: mExpr(expr)
	{
	}

	// TODO: Better way?
	SpectralBlob eval(const ShadingContext& ctx) const override
	{
		mExpr->updateVaryings(ctx);

		// Calculate each wavelength (not really the best solution)
		SpectralBlob output;
		for (size_t i = 0; i < PR_SPECTRAL_BLOB_SIZE; ++i) {
			mExpr->updateSpectralVaryings(i, ctx);

			const double* result = mExpr->Expr.evalFP(&mExpr->VarBlocks[ctx.ThreadIndex]);
			output[i]			 = result[0];
		}
		return output;
	}

	// TODO: Get a better way to achieve this!
	Vector2i queryRecommendedSize() const override
	{
		return Vector2i(1, 1);
	}

	void beforeRender(RenderContext* ctx) override
	{
		FloatSpectralNode::beforeRender(ctx);
		mExpr->setupThreadData(ctx->threads());
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Spectral Expression (" << mExpr->Expr.getExpr() << ")";
		return sstream.str();
	}

private:
	mutable std::shared_ptr<ExpressionContainer> mExpr;
};

class VectorExpressionNode : public FloatVectorNode {
public:
	explicit VectorExpressionNode(const std::shared_ptr<ExpressionContainer>& expr)
		: mExpr(expr)
	{
	}

	Vector3f eval(const ShadingContext& ctx) const override
	{
		mExpr->updateVaryings(ctx);
		const double* result = mExpr->Expr.evalFP(&mExpr->VarBlocks[ctx.ThreadIndex]);
		return Vector3f(result[0], result[1], result[2]);
	}

	void beforeRender(RenderContext* ctx) override
	{
		FloatVectorNode::beforeRender(ctx);
		mExpr->setupThreadData(ctx->threads());
	}

	std::string dumpInformation() const override
	{
		std::stringstream sstream;
		sstream << "Vector3f Expression (" << mExpr->Expr.getExpr() << ")";
		return sstream.str();
	}

private:
	mutable std::shared_ptr<ExpressionContainer> mExpr;
};

class ExpressionPlugin : public INodePlugin {
public:
	std::shared_ptr<INode> create(uint32, const std::string& type_name, const SceneLoadContext& ctx) override
	{
		std::string expr_str;
		if (type_name == "expr" || type_name == "vexpr") {
			expr_str = ctx.Parameters.getString("expression", "");
			if (expr_str.empty())
				expr_str = ctx.Parameters.getString(0, "");
		} else {
			std::string path = ctx.Parameters.getString("file", "");
			if (path.empty())
				path = ctx.Parameters.getString(0, "");

			std::ifstream stream(path, std::ios::in);
			if (!stream.good()) {
				PR_LOG(L_ERROR) << "[SeExpr2] Can not load file " << path << std::endl;
				return nullptr;
			}
			expr_str = std::string((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
		}

		const bool isVec = (type_name == "vexpr" || type_name == "fvexpr");
		auto expr		 = std::make_shared<ExpressionContainer>(expr_str, isVec);

		// Setup user variables
		for (const auto& entry : ctx.Parameters.parameters()) {
			const auto& param = entry.second;

			switch (param.type()) {
			case PT_Invalid:
				continue;
			case PT_Bool:
				expr->registerConstant(entry.first, param.getBool(false) ? 1.0f : 0.0f);
				break;
			case PT_Int:
			case PT_UInt:
			case PT_Number:
				expr->registerConstant(entry.first, param.getNumber(0.0f));
				break;
			case PT_String:
			case PT_Reference: {
				auto node = ctx.Env->lookupRawNode(param);
				switch (node->type()) {
				case NT_FloatScalar:
					expr->registerVarying(entry.first, std::reinterpret_pointer_cast<FloatScalarNode>(node));
					break;
				case NT_FloatSpectral:
					expr->registerVarying(entry.first, std::reinterpret_pointer_cast<FloatSpectralNode>(node));
					break;
				case NT_FloatVector:
					expr->registerVarying(entry.first, std::reinterpret_pointer_cast<FloatVectorNode>(node));
					break;
				}
			} break;
			}
		}

		if (!expr->Expr.isValid()) {
			PR_LOG(L_ERROR) << "[SeExpr2] Parsing Error: " << expr->Expr.parseError() << std::endl;
			return nullptr;
		}

		if (expr->isSpectralVarying() && isVec) {
			PR_LOG(L_ERROR) << "[SeExpr2] A spectral varying expression can not be combined with a vector expression" << std::endl;
			return nullptr;
		}

		if (isVec) {
			return std::make_shared<VectorExpressionNode>(expr);
		} else if (expr->isSpectralVarying()) {
			return std::make_shared<SpectralExpressionNode>(expr);
		} else {
			return std::make_shared<ScalarExpressionNode>(expr);
		}
	}

	const std::vector<std::string>& getNames() const override
	{
		const static std::vector<std::string> names({ "expr", "vexpr", "fexpr", "fvexpr" });
		return names;
	}

	bool init() override
	{
		return true;
	}
};
} // namespace PR

PR_PLUGIN_INIT(PR::ExpressionPlugin, _PR_PLUGIN_NAME, PR_PLUGIN_VERSION)<|endoftext|>


<commit_before><commit_msg>Ignore PixMap::plane_bytes<commit_after><|endoftext|>


<commit_before>#include <Engine/Renderer/Mesh/Mesh.hpp>

#include <numeric>

#include <Core/Mesh/HalfEdge.hpp>
#include <Core/Mesh/MeshUtils.hpp>
#include <Engine/Renderer/OpenGL/OpenGL.hpp>

#include <Core/Log/Log.hpp>
namespace Ra {
namespace Engine {

// Template parameter must be a Core::VectorNArray
template <typename ContainedType>
inline void sendGLData( Ra::Engine::Mesh* mesh, const Ra::Core::VectorArray<ContainedType>& arr,
                        const uint vboIdx ) {
    using VecArray = Ra::Core::VectorArray<ContainedType>;
#ifdef CORE_USE_DOUBLE
    GLenum type = GL_DOUBLE;
#else
    GLenum type = GL_FLOAT;
#endif
    constexpr GLuint size = VecArray::Vector::RowsAtCompileTime;
    const GLboolean normalized = GL_FALSE;
    constexpr GLint64 ptr = 0;

    // This vbo has not been created yet
    if ( mesh->m_vbos[vboIdx] == 0 && arr.size() > 0 )
    {
        GL_ASSERT( glGenBuffers( 1, &( mesh->m_vbos[vboIdx] ) ) );
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, mesh->m_vbos[vboIdx] ) );

        // Use (vboIdx - 1) as attribute index because vbo 0 is actually ibo.
        GL_ASSERT( glVertexAttribPointer( vboIdx - 1, size, type, normalized,
                                          sizeof( typename VecArray::Vector ), (GLvoid*)ptr ) );

        GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        // Set dirty as true to send data, see below
        mesh->m_dataDirty[vboIdx] = true;
    }

    if ( mesh->m_dataDirty[vboIdx] == true && mesh->m_vbos[vboIdx] != 0 )
    {
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, mesh->m_vbos[vboIdx] ) );
        GL_ASSERT( glBufferData( GL_ARRAY_BUFFER, arr.size() * sizeof( typename VecArray::Vector ),
                                 arr.data(), GL_DYNAMIC_DRAW ) );
        mesh->m_dataDirty[vboIdx] = false;

        if ( arr.size() > 0 )
        {
            GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        } else
        { GL_ASSERT( glDisableVertexAttribArray( vboIdx - 1 ) ); }
    }
} // sendGLData

// Dirty is initializes as false so that we do not create the vao while
// we have no data to send to the gpu.
Mesh::Mesh( const std::string& name, MeshRenderMode renderMode ) :
    m_name( name ),
    m_vao( 0 ),
    m_renderMode( renderMode ),
    m_numElements( 0 ),
    m_isDirty( false ) {
    CORE_ASSERT( m_renderMode == RM_POINTS || m_renderMode == RM_LINES ||
                     m_renderMode == RM_LINE_LOOP || m_renderMode == RM_LINE_STRIP ||
                     m_renderMode == RM_TRIANGLES || m_renderMode == RM_TRIANGLE_STRIP ||
                     m_renderMode == RM_TRIANGLE_FAN || m_renderMode == RM_LINES_ADJACENCY ||
                     m_renderMode == RM_LINE_STRIP_ADJACENCY,
                 "Unsupported render mode" );
}

Mesh::~Mesh() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glDeleteVertexArrays( 1, &m_vao ) );

        for ( auto& vbo : m_vbos )
        {
            if ( vbo != 0 )
            {
                glDeleteBuffers( 1, &vbo );
            }
        }
    }
}

void Mesh::render() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glBindVertexArray( m_vao ) );
        GL_ASSERT( glDrawElements( static_cast<GLenum>( m_renderMode ), m_numElements,
                                   GL_UNSIGNED_INT, (void*)0 ) );
    }
}

void Mesh::loadGeometry( const Core::TriangleMesh& mesh ) {
    m_mesh = mesh;

    if ( m_mesh.m_triangles.empty() )
    {
        m_numElements = m_mesh.vertices().size();
        m_renderMode = RM_POINTS;
    } else
        m_numElements = m_mesh.m_triangles.size() * 3;

    for ( uint i = 0; i < MAX_VEC3; ++i )
    {
        m_v3DataHandle[i] = m_mesh.getAttribHandle<Ra::Core::Vector3>( std::string( "Vec3_attr_" ) +
                                                                       std::to_string( i ) );
    }

    for ( uint i = 0; i < MAX_VEC4; ++i )
    {
        m_v4DataHandle[i] = m_mesh.getAttribHandle<Ra::Core::Vector4>( std::string( "Vec4_attr_" ) +
                                                                       std::to_string( i ) );
    }

    for ( uint i = 0; i < MAX_DATA; ++i )
    {
        m_dataDirty[i] = true;
    }

    m_isDirty = true;
}

void Mesh::updateMeshGeometry( MeshData type, const Core::Vector3Array& data ) {
    if ( type == VERTEX_POSITION )
        m_mesh.vertices() = data;
    if ( type == VERTEX_NORMAL )
        m_mesh.normals() = data;
    m_dataDirty[static_cast<uint>( type )] = true;
    m_isDirty = true;
}

void Mesh::loadGeometry( const Core::Vector3Array& vertices, const std::vector<uint>& indices ) {
    // Do not remove this function to force everyone to use triangle mesh.
    //  ... because we have some line meshes as well...
    const uint nIdx = indices.size();

    if ( indices.empty() )
    {
        m_numElements = vertices.size();
        m_renderMode = RM_POINTS;
    } else
        m_numElements = nIdx;
    m_mesh.vertices() = vertices;

    // Check that when loading a triangle mesh we actually have triangles or lines.
    CORE_ASSERT( m_renderMode != GL_TRIANGLES || nIdx % 3 == 0,
                 "There should be 3 indices per triangle " );
    CORE_ASSERT( m_renderMode != GL_LINES || nIdx % 2 == 0, "There should be 2 indices per line" );
    CORE_ASSERT( m_renderMode != GL_LINES_ADJACENCY || nIdx % 4 == 0,
                 "There should be 4 indices per line adjacency" );

    for ( uint i = 0; i < indices.size(); i = i + 3 )
    {
        // We store all indices in order. This means that for lines we have
        // (L00, L01, L10), (L11, L20, L21) etc. We fill the missing by wrapping around indices.
        m_mesh.m_triangles.push_back(
            {indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx]} );
    }

    // Mark mesh as dirty.
    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::addData( const Vec3Data& type, const Core::Vector3Array& data ) {
    const int index = static_cast<uint>( type );
    auto& handle = m_v3DataHandle[index];

    // if it's the first time this handle is used, add it to m_mesh.
    if ( data.size() != 0 && !m_mesh.isValid( handle ) )
    {
        handle =
            m_mesh.addAttrib<Core::Vector3>( std::string( "Vec3_attr_" ) + std::to_string( type ) );
    }

    //    if ( data.size() != 0 && m_mesh.isValid( handle ) )
    if ( m_mesh.isValid( handle ) )
    {
        m_mesh.getAttrib( handle ).data() = data;

        m_dataDirty[MAX_MESH + index] = true;
        m_isDirty = true;
    }
}

void Mesh::addData( const Vec4Data& type, const Core::Vector4Array& data ) {

    const int index = static_cast<uint>( type );
    auto& handle = m_v4DataHandle[index];

    // if it's the first time this handle is used, add it to m_mesh.
    if ( data.size() != 0 && !m_mesh.isValid( handle ) )
    {
        handle =
            m_mesh.addAttrib<Core::Vector4>( std::string( "Vec4_attr_" ) + std::to_string( type ) );
    }

    //    if ( data.size() != 0 && m_mesh.isValid( handle ) )
    if ( m_mesh.isValid( handle ) )
    {
        m_mesh.getAttrib( handle ).data() = data;
        m_dataDirty[MAX_MESH + MAX_VEC3 + index] = true;
        m_isDirty = true;
    }
}

void Mesh::updateGL() {
    if ( m_isDirty )
    {
        // Check that our dirty bits are consistent.
        ON_ASSERT( bool dirtyTest = false; for ( const auto& d
                                                 : m_dataDirty ) { dirtyTest = dirtyTest || d; } );
        CORE_ASSERT( dirtyTest == m_isDirty, "Dirty flags inconsistency" );

        CORE_ASSERT( !( m_mesh.vertices().empty() ), "No vertex." );

        if ( m_vao == 0 )
        {
            // Create VAO if it does not exist
            GL_ASSERT( glGenVertexArrays( 1, &m_vao ) );
        }

        // Bind it
        GL_ASSERT( glBindVertexArray( m_vao ) );

        if ( m_vbos[INDEX] == 0 )
        {
            GL_ASSERT( glGenBuffers( 1, &m_vbos[INDEX] ) );
            GL_ASSERT( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX] ) );
        }
        if ( m_dataDirty[INDEX] )
        {
            if ( m_renderMode == RM_POINTS )
            {
                std::vector<int> indices( m_numElements );
                std::iota( indices.begin(), indices.end(), 0 );
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_numElements * sizeof( int ),
                                         indices.data(), GL_DYNAMIC_DRAW ) );
            } else
            {
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER,
                                         m_mesh.m_triangles.size() * sizeof( Ra::Core::Triangle ),
                                         m_mesh.m_triangles.data(), GL_DYNAMIC_DRAW ) );
            }
            m_dataDirty[INDEX] = false;
        }

        // Geometry data
        sendGLData( this, m_mesh.vertices(), VERTEX_POSITION );
        sendGLData( this, m_mesh.normals(), VERTEX_NORMAL );

        for ( int i = 0; i < MAX_VEC3; i++ )
        {
            if ( m_mesh.isValid( m_v3DataHandle[i] ) )
            {
                sendGLData( this, m_mesh.getAttrib( m_v3DataHandle[i] ).data(), MAX_MESH + i );
            }
        }

        for ( int i = 0; i < MAX_VEC4; i++ )
        {
            if ( m_mesh.isValid( m_v4DataHandle[i] ) )
            {
                sendGLData( this, m_mesh.getAttrib( m_v4DataHandle[i] ).data(),
                            MAX_MESH + MAX_VEC3 + i );
            }
        }

        GL_ASSERT( glBindVertexArray( 0 ) );
        GL_CHECK_ERROR;
        m_isDirty = false;
    }
}

} // namespace Engine
} // namespace Ra
<commit_msg>Initialize data dirty bits to false in constructor ;<commit_after>#include <Engine/Renderer/Mesh/Mesh.hpp>

#include <numeric>

#include <Core/Mesh/HalfEdge.hpp>
#include <Core/Mesh/MeshUtils.hpp>
#include <Engine/Renderer/OpenGL/OpenGL.hpp>

#include <Core/Log/Log.hpp>
namespace Ra {
namespace Engine {

// Template parameter must be a Core::VectorNArray
template <typename ContainedType>
inline void sendGLData( Ra::Engine::Mesh* mesh, const Ra::Core::VectorArray<ContainedType>& arr,
                        const uint vboIdx ) {
    using VecArray = Ra::Core::VectorArray<ContainedType>;
#ifdef CORE_USE_DOUBLE
    GLenum type = GL_DOUBLE;
#else
    GLenum type = GL_FLOAT;
#endif
    constexpr GLuint size = VecArray::Vector::RowsAtCompileTime;
    const GLboolean normalized = GL_FALSE;
    constexpr GLint64 ptr = 0;

    // This vbo has not been created yet
    if ( mesh->m_vbos[vboIdx] == 0 && arr.size() > 0 )
    {
        GL_ASSERT( glGenBuffers( 1, &( mesh->m_vbos[vboIdx] ) ) );
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, mesh->m_vbos[vboIdx] ) );

        // Use (vboIdx - 1) as attribute index because vbo 0 is actually ibo.
        GL_ASSERT( glVertexAttribPointer( vboIdx - 1, size, type, normalized,
                                          sizeof( typename VecArray::Vector ), (GLvoid*)ptr ) );

        GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        // Set dirty as true to send data, see below
        mesh->m_dataDirty[vboIdx] = true;
    }

    if ( mesh->m_dataDirty[vboIdx] == true && mesh->m_vbos[vboIdx] != 0 )
    {
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, mesh->m_vbos[vboIdx] ) );
        GL_ASSERT( glBufferData( GL_ARRAY_BUFFER, arr.size() * sizeof( typename VecArray::Vector ),
                                 arr.data(), GL_DYNAMIC_DRAW ) );
        mesh->m_dataDirty[vboIdx] = false;

        if ( arr.size() > 0 )
        {
            GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        } else
        { GL_ASSERT( glDisableVertexAttribArray( vboIdx - 1 ) ); }
    }
} // sendGLData

// Dirty is initializes as false so that we do not create the vao while
// we have no data to send to the gpu.
Mesh::Mesh( const std::string& name, MeshRenderMode renderMode ) :
    m_name( name ),
    m_vao( 0 ),
    m_renderMode( renderMode ),
    m_numElements( 0 ),
    m_isDirty( false ) {
    CORE_ASSERT( m_renderMode == RM_POINTS || m_renderMode == RM_LINES ||
                     m_renderMode == RM_LINE_LOOP || m_renderMode == RM_LINE_STRIP ||
                     m_renderMode == RM_TRIANGLES || m_renderMode == RM_TRIANGLE_STRIP ||
                     m_renderMode == RM_TRIANGLE_FAN || m_renderMode == RM_LINES_ADJACENCY ||
                     m_renderMode == RM_LINE_STRIP_ADJACENCY,
                 "Unsupported render mode" );

    // Mark mesh data as up-to-date.
    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = false;
    }
}

Mesh::~Mesh() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glDeleteVertexArrays( 1, &m_vao ) );

        for ( auto& vbo : m_vbos )
        {
            if ( vbo != 0 )
            {
                glDeleteBuffers( 1, &vbo );
            }
        }
    }
}

void Mesh::render() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glBindVertexArray( m_vao ) );
        GL_ASSERT( glDrawElements( static_cast<GLenum>( m_renderMode ), m_numElements,
                                   GL_UNSIGNED_INT, (void*)0 ) );
    }
}

void Mesh::loadGeometry( const Core::TriangleMesh& mesh ) {
    m_mesh = mesh;

    if ( m_mesh.m_triangles.empty() )
    {
        m_numElements = m_mesh.vertices().size();
        m_renderMode = RM_POINTS;
    } else
        m_numElements = m_mesh.m_triangles.size() * 3;

    for ( uint i = 0; i < MAX_VEC3; ++i )
    {
        m_v3DataHandle[i] = m_mesh.getAttribHandle<Ra::Core::Vector3>( std::string( "Vec3_attr_" ) +
                                                                       std::to_string( i ) );
    }

    for ( uint i = 0; i < MAX_VEC4; ++i )
    {
        m_v4DataHandle[i] = m_mesh.getAttribHandle<Ra::Core::Vector4>( std::string( "Vec4_attr_" ) +
                                                                       std::to_string( i ) );
    }

    for ( uint i = 0; i < MAX_DATA; ++i )
    {
        m_dataDirty[i] = true;
    }

    m_isDirty = true;
}

void Mesh::updateMeshGeometry( MeshData type, const Core::Vector3Array& data ) {
    if ( type == VERTEX_POSITION )
        m_mesh.vertices() = data;
    if ( type == VERTEX_NORMAL )
        m_mesh.normals() = data;
    m_dataDirty[static_cast<uint>( type )] = true;
    m_isDirty = true;
}

void Mesh::loadGeometry( const Core::Vector3Array& vertices, const std::vector<uint>& indices ) {
    // Do not remove this function to force everyone to use triangle mesh.
    //  ... because we have some line meshes as well...
    const uint nIdx = indices.size();

    if ( indices.empty() )
    {
        m_numElements = vertices.size();
        m_renderMode = RM_POINTS;
    } else
        m_numElements = nIdx;
    m_mesh.vertices() = vertices;

    // Check that when loading a triangle mesh we actually have triangles or lines.
    CORE_ASSERT( m_renderMode != GL_TRIANGLES || nIdx % 3 == 0,
                 "There should be 3 indices per triangle " );
    CORE_ASSERT( m_renderMode != GL_LINES || nIdx % 2 == 0, "There should be 2 indices per line" );
    CORE_ASSERT( m_renderMode != GL_LINES_ADJACENCY || nIdx % 4 == 0,
                 "There should be 4 indices per line adjacency" );

    for ( uint i = 0; i < indices.size(); i = i + 3 )
    {
        // We store all indices in order. This means that for lines we have
        // (L00, L01, L10), (L11, L20, L21) etc. We fill the missing by wrapping around indices.
        m_mesh.m_triangles.push_back(
            {indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx]} );
    }

    // Mark mesh as dirty.
    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::addData( const Vec3Data& type, const Core::Vector3Array& data ) {
    const int index = static_cast<uint>( type );
    auto& handle = m_v3DataHandle[index];

    // if it's the first time this handle is used, add it to m_mesh.
    if ( data.size() != 0 && !m_mesh.isValid( handle ) )
    {
        handle =
            m_mesh.addAttrib<Core::Vector3>( std::string( "Vec3_attr_" ) + std::to_string( type ) );
    }

    //    if ( data.size() != 0 && m_mesh.isValid( handle ) )
    if ( m_mesh.isValid( handle ) )
    {
        m_mesh.getAttrib( handle ).data() = data;

        m_dataDirty[MAX_MESH + index] = true;
        m_isDirty = true;
    }
}

void Mesh::addData( const Vec4Data& type, const Core::Vector4Array& data ) {

    const int index = static_cast<uint>( type );
    auto& handle = m_v4DataHandle[index];

    // if it's the first time this handle is used, add it to m_mesh.
    if ( data.size() != 0 && !m_mesh.isValid( handle ) )
    {
        handle =
            m_mesh.addAttrib<Core::Vector4>( std::string( "Vec4_attr_" ) + std::to_string( type ) );
    }

    //    if ( data.size() != 0 && m_mesh.isValid( handle ) )
    if ( m_mesh.isValid( handle ) )
    {
        m_mesh.getAttrib( handle ).data() = data;
        m_dataDirty[MAX_MESH + MAX_VEC3 + index] = true;
        m_isDirty = true;
    }
}

void Mesh::updateGL() {
    if ( m_isDirty )
    {
        // Check that our dirty bits are consistent.
        ON_ASSERT( bool dirtyTest = false; for ( const auto& d
                                                 : m_dataDirty ) { dirtyTest = dirtyTest || d; } );
        CORE_ASSERT( dirtyTest == m_isDirty, "Dirty flags inconsistency" );

        CORE_ASSERT( !( m_mesh.vertices().empty() ), "No vertex." );

        if ( m_vao == 0 )
        {
            // Create VAO if it does not exist
            GL_ASSERT( glGenVertexArrays( 1, &m_vao ) );
        }

        // Bind it
        GL_ASSERT( glBindVertexArray( m_vao ) );

        if ( m_vbos[INDEX] == 0 )
        {
            GL_ASSERT( glGenBuffers( 1, &m_vbos[INDEX] ) );
            GL_ASSERT( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX] ) );
        }
        if ( m_dataDirty[INDEX] )
        {
            if ( m_renderMode == RM_POINTS )
            {
                std::vector<int> indices( m_numElements );
                std::iota( indices.begin(), indices.end(), 0 );
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_numElements * sizeof( int ),
                                         indices.data(), GL_DYNAMIC_DRAW ) );
            } else
            {
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER,
                                         m_mesh.m_triangles.size() * sizeof( Ra::Core::Triangle ),
                                         m_mesh.m_triangles.data(), GL_DYNAMIC_DRAW ) );
            }
            m_dataDirty[INDEX] = false;
        }

        // Geometry data
        sendGLData( this, m_mesh.vertices(), VERTEX_POSITION );
        sendGLData( this, m_mesh.normals(), VERTEX_NORMAL );

        for ( int i = 0; i < MAX_VEC3; i++ )
        {
            if ( m_mesh.isValid( m_v3DataHandle[i] ) )
            {
                sendGLData( this, m_mesh.getAttrib( m_v3DataHandle[i] ).data(), MAX_MESH + i );
            }
        }

        for ( int i = 0; i < MAX_VEC4; i++ )
        {
            if ( m_mesh.isValid( m_v4DataHandle[i] ) )
            {
                sendGLData( this, m_mesh.getAttrib( m_v4DataHandle[i] ).data(),
                            MAX_MESH + MAX_VEC3 + i );
            }
        }

        GL_ASSERT( glBindVertexArray( 0 ) );
        GL_CHECK_ERROR;
        m_isDirty = false;
    }
}

} // namespace Engine
} // namespace Ra
<|endoftext|>


<commit_before><commit_msg>SVN_SILENT This should make SK to compile without -DQT3_SUPPORT<commit_after><|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "clonewizardpage.h"
#include "gitplugin.h"
#include "gitclient.h"

#include <vcsbase/command.h>

namespace Git {

struct CloneWizardPagePrivate {
    CloneWizardPagePrivate();

    bool urlIsLocal(const QString &url);

    const QString mainLinePostfix;
    const QString gitPostFix;
    const QString protocolDelimiter;
};

CloneWizardPagePrivate::CloneWizardPagePrivate() :
    mainLinePostfix(QLatin1String("/mainline.git")),
    gitPostFix(QLatin1String(".git")),
    protocolDelimiter(QLatin1String("://"))
{
}

bool CloneWizardPagePrivate::urlIsLocal(const QString &url)
{
    if (url.startsWith(QLatin1String("file://"))
        || url.startsWith(QLatin1Char('/'))
        || (url.at(0).isLetter() && url.at(1) == QLatin1Char(':') && url.at(2) == QLatin1Char('\\')))
        return true;
    return false;
}

CloneWizardPage::CloneWizardPage(QWidget *parent) :
    VcsBase::BaseCheckoutWizardPage(parent),
    d(new CloneWizardPagePrivate)
{
    setTitle(tr("Location"));
    setSubTitle(tr("Specify repository URL, checkout directory and path."));
    setRepositoryLabel(tr("Clone URL:"));
}

CloneWizardPage::~CloneWizardPage()
{
    delete d;
}

QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
{
    /* Try to figure out a good directory name from something like:
     * 'user@host:qt/qt.git', 'http://host/qt/qt.git' 'local repo'
     * ------> 'qt' .  */
    const QChar slash = QLatin1Char('/');
    QString url = urlIn.trimmed().replace(QLatin1Char('\\'), slash);

    // remove host
    const int protocolDelimiterPos = url.indexOf(d->protocolDelimiter); // "://"
    const int startRepoSearchPos = protocolDelimiterPos == -1 ? 0 : protocolDelimiterPos + d->protocolDelimiter.size();
    int repoPos = url.indexOf(QLatin1Char(':'), startRepoSearchPos);
    if (repoPos == -1)
        repoPos = url.indexOf(slash, startRepoSearchPos);
    if (repoPos != -1)
        url.remove(0, repoPos + 1);
    // Remove postfixes
    if (url.endsWith(d->mainLinePostfix)) {
        url.truncate(url.size() - d->mainLinePostfix.size());
    } else {
        if (url.endsWith(d->gitPostFix))
            url.truncate(url.size() - d->gitPostFix.size());
    }
    // Check for equal parts, something like "qt/qt" -> "qt"
    const int slashPos = url.indexOf(slash);
    if (slashPos != -1 && slashPos == (url.size() - 1) / 2) {
        if (url.leftRef(slashPos) == url.rightRef(slashPos))
            url.truncate(slashPos);
    }
    // fix invalid characters
    const QChar dash = QLatin1Char('-');
    url.replace(QRegExp(QLatin1String("[^0-9a-zA-Z_.-]")), dash);
    // trim leading dashes (they are annoying and get created when using local pathes)
    url.replace(QRegExp(QLatin1String("^-+")), QString());
    return url;
}

VcsBase::Command *CloneWizardPage::createCheckoutJob(QString *checkoutPath) const
{
     const Internal::GitClient *client = Internal::GitPlugin::instance()->gitClient();
     const QString workingDirectory = path();
     const QString checkoutDir = directory();
     *checkoutPath = workingDirectory + QLatin1Char('/') + checkoutDir;

     const QString checkoutBranch = branch();

     QStringList args(QLatin1String("clone"));
     if (!checkoutBranch.isEmpty())
         args << QLatin1String("--branch") << checkoutBranch;
     args << repository() << checkoutDir;
     VcsBase::Command *command = new VcsBase::Command(client->gitBinaryPath(), workingDirectory,
                                                      client->processEnvironment());
     command->addFlags(VcsBase::VcsBasePlugin::MergeOutputChannels);
     command->addJob(args, -1);
     return command;
}

QStringList CloneWizardPage::branches(const QString &repository, int *current)
{
    // Run git on remote repository if an URL was specified.
    *current = -1;

    if (repository.isEmpty())
        return QStringList();
     const QStringList branches = Internal::GitPlugin::instance()->gitClient()->synchronousRepositoryBranches(repository);
     if (!branches.isEmpty())
         *current = 0; // default branch is always returned first!
     return branches;
}

} // namespace Git
<commit_msg>Git: Show progress on clone<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "clonewizardpage.h"
#include "gitplugin.h"
#include "gitclient.h"

#include <vcsbase/command.h>

namespace Git {

struct CloneWizardPagePrivate {
    CloneWizardPagePrivate();

    bool urlIsLocal(const QString &url);

    const QString mainLinePostfix;
    const QString gitPostFix;
    const QString protocolDelimiter;
};

CloneWizardPagePrivate::CloneWizardPagePrivate() :
    mainLinePostfix(QLatin1String("/mainline.git")),
    gitPostFix(QLatin1String(".git")),
    protocolDelimiter(QLatin1String("://"))
{
}

bool CloneWizardPagePrivate::urlIsLocal(const QString &url)
{
    if (url.startsWith(QLatin1String("file://"))
        || url.startsWith(QLatin1Char('/'))
        || (url.at(0).isLetter() && url.at(1) == QLatin1Char(':') && url.at(2) == QLatin1Char('\\')))
        return true;
    return false;
}

CloneWizardPage::CloneWizardPage(QWidget *parent) :
    VcsBase::BaseCheckoutWizardPage(parent),
    d(new CloneWizardPagePrivate)
{
    setTitle(tr("Location"));
    setSubTitle(tr("Specify repository URL, checkout directory and path."));
    setRepositoryLabel(tr("Clone URL:"));
}

CloneWizardPage::~CloneWizardPage()
{
    delete d;
}

QString CloneWizardPage::directoryFromRepository(const QString &urlIn) const
{
    /* Try to figure out a good directory name from something like:
     * 'user@host:qt/qt.git', 'http://host/qt/qt.git' 'local repo'
     * ------> 'qt' .  */
    const QChar slash = QLatin1Char('/');
    QString url = urlIn.trimmed().replace(QLatin1Char('\\'), slash);

    // remove host
    const int protocolDelimiterPos = url.indexOf(d->protocolDelimiter); // "://"
    const int startRepoSearchPos = protocolDelimiterPos == -1 ? 0 : protocolDelimiterPos + d->protocolDelimiter.size();
    int repoPos = url.indexOf(QLatin1Char(':'), startRepoSearchPos);
    if (repoPos == -1)
        repoPos = url.indexOf(slash, startRepoSearchPos);
    if (repoPos != -1)
        url.remove(0, repoPos + 1);
    // Remove postfixes
    if (url.endsWith(d->mainLinePostfix)) {
        url.truncate(url.size() - d->mainLinePostfix.size());
    } else {
        if (url.endsWith(d->gitPostFix))
            url.truncate(url.size() - d->gitPostFix.size());
    }
    // Check for equal parts, something like "qt/qt" -> "qt"
    const int slashPos = url.indexOf(slash);
    if (slashPos != -1 && slashPos == (url.size() - 1) / 2) {
        if (url.leftRef(slashPos) == url.rightRef(slashPos))
            url.truncate(slashPos);
    }
    // fix invalid characters
    const QChar dash = QLatin1Char('-');
    url.replace(QRegExp(QLatin1String("[^0-9a-zA-Z_.-]")), dash);
    // trim leading dashes (they are annoying and get created when using local pathes)
    url.replace(QRegExp(QLatin1String("^-+")), QString());
    return url;
}

VcsBase::Command *CloneWizardPage::createCheckoutJob(QString *checkoutPath) const
{
     const Internal::GitClient *client = Internal::GitPlugin::instance()->gitClient();
     const QString workingDirectory = path();
     const QString checkoutDir = directory();
     *checkoutPath = workingDirectory + QLatin1Char('/') + checkoutDir;

     const QString checkoutBranch = branch();

     QStringList args(QLatin1String("clone"));
     if (!checkoutBranch.isEmpty())
         args << QLatin1String("--branch") << checkoutBranch;
     args << QLatin1String("--progress") << repository() << checkoutDir;
     VcsBase::Command *command = new VcsBase::Command(client->gitBinaryPath(), workingDirectory,
                                                      client->processEnvironment());
     command->addFlags(VcsBase::VcsBasePlugin::MergeOutputChannels);
     command->addJob(args, -1);
     return command;
}

QStringList CloneWizardPage::branches(const QString &repository, int *current)
{
    // Run git on remote repository if an URL was specified.
    *current = -1;

    if (repository.isEmpty())
        return QStringList();
     const QStringList branches = Internal::GitPlugin::instance()->gitClient()->synchronousRepositoryBranches(repository);
     if (!branches.isEmpty())
         *current = 0; // default branch is always returned first!
     return branches;
}

} // namespace Git
<|endoftext|>


<commit_before>#include <Engine/Renderer/Mesh/Mesh.hpp>

#include <numeric>

#include <Core/Mesh/HalfEdge.hpp>
#include <Core/Mesh/MeshUtils.hpp>
#include <Engine/Renderer/OpenGL/OpenGL.hpp>
namespace Ra {
namespace Engine {

// Dirty is initializes as false so that we do not create the vao while
// we have no data to send to the gpu.
Mesh::Mesh( const std::string& name, MeshRenderMode renderMode ) :
    m_name( name ),
    m_vao( 0 ),
    m_renderMode( renderMode ),
    m_numElements( 0 ),
    m_isDirty( false ) {
    CORE_ASSERT( m_renderMode == RM_POINTS || m_renderMode == RM_LINES ||
                     m_renderMode == RM_LINE_LOOP || m_renderMode == RM_LINE_STRIP ||
                     m_renderMode == RM_TRIANGLES || m_renderMode == RM_TRIANGLE_STRIP ||
                     m_renderMode == RM_TRIANGLE_FAN || m_renderMode == RM_LINES_ADJACENCY ||
                     m_renderMode == RM_LINE_STRIP_ADJACENCY,
                 "Unsupported render mode" );
}

Mesh::~Mesh() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glDeleteVertexArrays( 1, &m_vao ) );
        for ( auto& vbo : m_vbos )
        {
            glDeleteBuffers( 1, &vbo );
        }
    }
}

void Mesh::render() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glBindVertexArray( m_vao ) );
        GL_ASSERT( glDrawElements( static_cast<GLenum>( m_renderMode ), m_numElements,
                                   GL_UNSIGNED_INT, (void*)0 ) );
    }
}

void Mesh::loadGeometry( const Core::TriangleMesh& mesh ) {
    m_mesh = mesh;

    if ( m_mesh.m_triangles.empty() )
    {
        m_numElements = mesh.vertices().size();
        m_renderMode = RM_POINTS;
    }
    else
        m_numElements = mesh.m_triangles.size() * 3;

    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::updateMeshGeometry( MeshData type, const Core::Vector3Array& data ) {
    if ( type == VERTEX_POSITION )
        m_mesh.vertices() = data;
    if ( type == VERTEX_NORMAL )
        m_mesh.normals() = data;
    m_dataDirty[static_cast<uint>( type )] = true;
    m_isDirty = true;
}

void Mesh::loadGeometry( const Core::Vector3Array& vertices, const std::vector<uint>& indices ) {
    // Do not remove this function to force everyone to use triangle mesh.
    //  ... because we have some line meshes as well...
    const uint nIdx = indices.size();

    if ( indices.empty() )
    {
        m_numElements = vertices.size();
        m_renderMode = RM_POINTS;
    }
    else
        m_numElements = nIdx;
    m_mesh.vertices() = vertices;

    // Check that when loading a triangle mesh we actually have triangles or lines.
    CORE_ASSERT( m_renderMode != GL_TRIANGLES || nIdx % 3 == 0,
                 "There should be 3 indices per triangle " );
    CORE_ASSERT( m_renderMode != GL_LINES || nIdx % 2 == 0, "There should be 2 indices per line" );
    CORE_ASSERT( m_renderMode != GL_LINES_ADJACENCY || nIdx % 4 == 0,
                 "There should be 4 indices per line adjacency" );

    for ( uint i = 0; i < indices.size(); i = i + 3 )
    {
        // We store all indices in order. This means that for lines we have
        // (L00, L01, L10), (L11, L20, L21) etc. We fill the missing by wrapping around indices.
        m_mesh.m_triangles.push_back(
            {indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx]} );
    }

    // Mark mesh as dirty.
    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::addData( const Vec3Data& type, const Core::Vector3Array& data ) {
    const int index = static_cast<uint>( type );
    if ( !m_v3DataHandle[index].isValid() )
    {
        m_v3DataHandle[index] =
            m_mesh.attribManager().addAttrib<Core::Vector3>( std::to_string( MAX_MESH + index ) );
    }
    m_mesh.attribManager().getAttrib( m_v3DataHandle[index] ).data() = data;
    m_dataDirty[MAX_MESH + index] = true;
    m_isDirty = true;
}

void Mesh::addData( const Vec4Data& type, const Core::Vector4Array& data ) {
    const int index = static_cast<uint>( type );

    /// to handle dummy set, if not valid, and dummy data, do nothing
    if ( data.size() == 0 && !m_v4DataHandle[index].isValid() )
    {
        return;
    }
    if ( !m_v4DataHandle[index].isValid() )
    {
        m_v4DataHandle[index] = m_mesh.attribManager().addAttrib<Core::Vector4>(
            std::to_string( MAX_MESH + MAX_VEC3 + index ) );
    }
    m_mesh.attribManager().getAttrib( m_v4DataHandle[index] ).data() = data;
    m_dataDirty[MAX_MESH + MAX_VEC3 + index] = true;
    m_isDirty = true;
}

// Template parameter must be a Core::VectorNArray
template <typename VecArray>
void Mesh::sendGLData( const VecArray& arr, const uint vboIdx ) {

#ifdef CORE_USE_DOUBLE
    GLenum type = GL_DOUBLE;
#else
    GLenum type = GL_FLOAT;
#endif
    constexpr GLuint size = VecArray::Vector::RowsAtCompileTime;
    const GLboolean normalized = GL_FALSE;
    constexpr GLint64 ptr = 0;

    // This vbo has not been created yet
    if ( m_vbos[vboIdx] == 0 && arr.size() > 0 )
    {
        GL_ASSERT( glGenBuffers( 1, &m_vbos[vboIdx] ) );
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, m_vbos[vboIdx] ) );

        // Use (vboIdx - 1) as attribute index because vbo 0 is actually ibo.
        GL_ASSERT( glVertexAttribPointer( vboIdx - 1, size, type, normalized,
                                          sizeof( typename VecArray::Vector ), (GLvoid*)ptr ) );

        GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        // Set dirty as true to send data, see below
        m_dataDirty[vboIdx] = true;
    }

    if ( m_dataDirty[vboIdx] == true && m_vbos[vboIdx] != 0 && arr.size() > 0 )
    {
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, m_vbos[vboIdx] ) );
        GL_ASSERT( glBufferData( GL_ARRAY_BUFFER, arr.size() * sizeof( typename VecArray::Vector ),
                                 arr.data(), GL_DYNAMIC_DRAW ) );
        m_dataDirty[vboIdx] = false;
    }
}

void Mesh::updateGL() {
    if ( m_isDirty )
    {
        // Check that our dirty bits are consistent.
        ON_ASSERT( bool dirtyTest = false; for ( const auto& d
                                                 : m_dataDirty ) { dirtyTest = dirtyTest || d; } );
        CORE_ASSERT( dirtyTest == m_isDirty, "Dirty flags inconsistency" );

        CORE_ASSERT( !( m_mesh.vertices().empty() ), "No vertex." );

        if ( m_vao == 0 )
        {
            // Create VAO if it does not exist
            GL_ASSERT( glGenVertexArrays( 1, &m_vao ) );
        }

        // Bind it
        GL_ASSERT( glBindVertexArray( m_vao ) );

        if ( m_vbos[INDEX] == 0 )
        {
            GL_ASSERT( glGenBuffers( 1, &m_vbos[INDEX] ) );
            GL_ASSERT( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX] ) );
        }
        if ( m_dataDirty[INDEX] )
        {
            if ( m_renderMode == RM_POINTS )
            {
                std::vector<int> indices( m_numElements );
                std::iota( indices.begin(), indices.end(), 0 );
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_numElements * sizeof( int ),
                                         indices.data(), GL_DYNAMIC_DRAW ) );
            }
            else
            {
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER,
                                         m_mesh.m_triangles.size() * sizeof( Ra::Core::Triangle ),
                                         m_mesh.m_triangles.data(), GL_DYNAMIC_DRAW ) );
            }
            m_dataDirty[INDEX] = false;
        }

        // Geometry data
        sendGLData( m_mesh.vertices(), VERTEX_POSITION );
        sendGLData( m_mesh.normals(), VERTEX_NORMAL );

        // Vec3 data

        if ( m_v3DataHandle[VERTEX_TANGENT].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_TANGENT] ).data(),
                        MAX_MESH + VERTEX_TANGENT );
        if ( m_v3DataHandle[VERTEX_BITANGENT].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_BITANGENT] ).data(),
                        MAX_MESH + VERTEX_BITANGENT );
        if ( m_v3DataHandle[VERTEX_TEXCOORD].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_TEXCOORD] ).data(),
                        MAX_MESH + VERTEX_TEXCOORD );

        // Vec4 data
        if ( m_v4DataHandle[VERTEX_COLOR].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_COLOR] ).data(),
                        MAX_MESH + MAX_VEC3 + VERTEX_COLOR );

        if ( m_v4DataHandle[VERTEX_WEIGHTS].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_WEIGHTS] ).data(),
                        MAX_MESH + MAX_VEC3 + VERTEX_WEIGHTS );

        if ( m_v4DataHandle[VERTEX_WEIGHT_IDX].isValid() )
            sendGLData(
                m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_WEIGHT_IDX] ).data(),
                MAX_MESH + MAX_VEC3 + VERTEX_WEIGHT_IDX );
        GL_ASSERT( glBindVertexArray( 0 ) );
        GL_CHECK_ERROR;
        m_isDirty = false;
    }
}

} // namespace Engine
} // namespace Ra
<commit_msg>Cleaning<commit_after>#include <Engine/Renderer/Mesh/Mesh.hpp>

#include <numeric>

#include <Core/Mesh/HalfEdge.hpp>
#include <Core/Mesh/MeshUtils.hpp>
#include <Engine/Renderer/OpenGL/OpenGL.hpp>
namespace Ra {
namespace Engine {

// Dirty is initializes as false so that we do not create the vao while
// we have no data to send to the gpu.
Mesh::Mesh( const std::string& name, MeshRenderMode renderMode ) :
    m_name( name ),
    m_vao( 0 ),
    m_renderMode( renderMode ),
    m_numElements( 0 ),
    m_isDirty( false ) {
    CORE_ASSERT( m_renderMode == RM_POINTS || m_renderMode == RM_LINES ||
                     m_renderMode == RM_LINE_LOOP || m_renderMode == RM_LINE_STRIP ||
                     m_renderMode == RM_TRIANGLES || m_renderMode == RM_TRIANGLE_STRIP ||
                     m_renderMode == RM_TRIANGLE_FAN || m_renderMode == RM_LINES_ADJACENCY ||
                     m_renderMode == RM_LINE_STRIP_ADJACENCY,
                 "Unsupported render mode" );
}

Mesh::~Mesh() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glDeleteVertexArrays( 1, &m_vao ) );

        for ( auto& vbo : m_vbos )
        {
            if ( vbo != 0 )
            {
                glDeleteBuffers( 1, &vbo );
            }
        }
    }
}

void Mesh::render() {
    if ( m_vao != 0 )
    {
        GL_ASSERT( glBindVertexArray( m_vao ) );
        GL_ASSERT( glDrawElements( static_cast<GLenum>( m_renderMode ), m_numElements,
                                   GL_UNSIGNED_INT, (void*)0 ) );
    }
}

void Mesh::loadGeometry( const Core::TriangleMesh& mesh ) {
    m_mesh = mesh;

    if ( m_mesh.m_triangles.empty() )
    {
        m_numElements = mesh.vertices().size();
        m_renderMode = RM_POINTS;
    }
    else
        m_numElements = mesh.m_triangles.size() * 3;

    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::updateMeshGeometry( MeshData type, const Core::Vector3Array& data ) {
    if ( type == VERTEX_POSITION )
        m_mesh.vertices() = data;
    if ( type == VERTEX_NORMAL )
        m_mesh.normals() = data;
    m_dataDirty[static_cast<uint>( type )] = true;
    m_isDirty = true;
}

void Mesh::loadGeometry( const Core::Vector3Array& vertices, const std::vector<uint>& indices ) {
    // Do not remove this function to force everyone to use triangle mesh.
    //  ... because we have some line meshes as well...
    const uint nIdx = indices.size();

    if ( indices.empty() )
    {
        m_numElements = vertices.size();
        m_renderMode = RM_POINTS;
    }
    else
        m_numElements = nIdx;
    m_mesh.vertices() = vertices;

    // Check that when loading a triangle mesh we actually have triangles or lines.
    CORE_ASSERT( m_renderMode != GL_TRIANGLES || nIdx % 3 == 0,
                 "There should be 3 indices per triangle " );
    CORE_ASSERT( m_renderMode != GL_LINES || nIdx % 2 == 0, "There should be 2 indices per line" );
    CORE_ASSERT( m_renderMode != GL_LINES_ADJACENCY || nIdx % 4 == 0,
                 "There should be 4 indices per line adjacency" );

    for ( uint i = 0; i < indices.size(); i = i + 3 )
    {
        // We store all indices in order. This means that for lines we have
        // (L00, L01, L10), (L11, L20, L21) etc. We fill the missing by wrapping around indices.
        m_mesh.m_triangles.push_back(
            {indices[i], indices[( i + 1 ) % nIdx], indices[( i + 2 ) % nIdx]} );
    }

    // Mark mesh as dirty.
    for ( uint i = 0; i < MAX_MESH; ++i )
    {
        m_dataDirty[i] = true;
    }
    m_isDirty = true;
}

void Mesh::addData( const Vec3Data& type, const Core::Vector3Array& data ) {
    const int index = static_cast<uint>( type );
    if ( !m_v3DataHandle[index].isValid() )
    {
        m_v3DataHandle[index] =
            m_mesh.attribManager().addAttrib<Core::Vector3>( std::to_string( MAX_MESH + index ) );
    }
    m_mesh.attribManager().getAttrib( m_v3DataHandle[index] ).data() = data;
    m_dataDirty[MAX_MESH + index] = true;
    m_isDirty = true;
}

void Mesh::addData( const Vec4Data& type, const Core::Vector4Array& data ) {
    const int index = static_cast<uint>( type );

    /// to handle dummy set, if not valid, and dummy data, do nothing
    if ( data.size() == 0 && !m_v4DataHandle[index].isValid() )
    {
        return;
    }
    if ( !m_v4DataHandle[index].isValid() )
    {
        m_v4DataHandle[index] = m_mesh.attribManager().addAttrib<Core::Vector4>(
            std::to_string( MAX_MESH + MAX_VEC3 + index ) );
    }
    m_mesh.attribManager().getAttrib( m_v4DataHandle[index] ).data() = data;
    m_dataDirty[MAX_MESH + MAX_VEC3 + index] = true;
    m_isDirty = true;
}

// Template parameter must be a Core::VectorNArray
template <typename VecArray>
void Mesh::sendGLData( const VecArray& arr, const uint vboIdx ) {

#ifdef CORE_USE_DOUBLE
    GLenum type = GL_DOUBLE;
#else
    GLenum type = GL_FLOAT;
#endif
    constexpr GLuint size = VecArray::Vector::RowsAtCompileTime;
    const GLboolean normalized = GL_FALSE;
    constexpr GLint64 ptr = 0;

    // This vbo has not been created yet
    if ( m_vbos[vboIdx] == 0 && arr.size() > 0 )
    {
        GL_ASSERT( glGenBuffers( 1, &m_vbos[vboIdx] ) );
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, m_vbos[vboIdx] ) );

        // Use (vboIdx - 1) as attribute index because vbo 0 is actually ibo.
        GL_ASSERT( glVertexAttribPointer( vboIdx - 1, size, type, normalized,
                                          sizeof( typename VecArray::Vector ), (GLvoid*)ptr ) );

        GL_ASSERT( glEnableVertexAttribArray( vboIdx - 1 ) );
        // Set dirty as true to send data, see below
        m_dataDirty[vboIdx] = true;
    }

    if ( m_dataDirty[vboIdx] == true && m_vbos[vboIdx] != 0 && arr.size() > 0 )
    {
        GL_ASSERT( glBindBuffer( GL_ARRAY_BUFFER, m_vbos[vboIdx] ) );
        GL_ASSERT( glBufferData( GL_ARRAY_BUFFER, arr.size() * sizeof( typename VecArray::Vector ),
                                 arr.data(), GL_DYNAMIC_DRAW ) );
        m_dataDirty[vboIdx] = false;
    }
}

void Mesh::updateGL() {
    if ( m_isDirty )
    {
        // Check that our dirty bits are consistent.
        ON_ASSERT( bool dirtyTest = false; for ( const auto& d
                                                 : m_dataDirty ) { dirtyTest = dirtyTest || d; } );
        CORE_ASSERT( dirtyTest == m_isDirty, "Dirty flags inconsistency" );

        CORE_ASSERT( !( m_mesh.vertices().empty() ), "No vertex." );

        if ( m_vao == 0 )
        {
            // Create VAO if it does not exist
            GL_ASSERT( glGenVertexArrays( 1, &m_vao ) );
        }

        // Bind it
        GL_ASSERT( glBindVertexArray( m_vao ) );

        if ( m_vbos[INDEX] == 0 )
        {
            GL_ASSERT( glGenBuffers( 1, &m_vbos[INDEX] ) );
            GL_ASSERT( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX] ) );
        }
        if ( m_dataDirty[INDEX] )
        {
            if ( m_renderMode == RM_POINTS )
            {
                std::vector<int> indices( m_numElements );
                std::iota( indices.begin(), indices.end(), 0 );
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_numElements * sizeof( int ),
                                         indices.data(), GL_DYNAMIC_DRAW ) );
            }
            else
            {
                GL_ASSERT( glBufferData( GL_ELEMENT_ARRAY_BUFFER,
                                         m_mesh.m_triangles.size() * sizeof( Ra::Core::Triangle ),
                                         m_mesh.m_triangles.data(), GL_DYNAMIC_DRAW ) );
            }
            m_dataDirty[INDEX] = false;
        }

        // Geometry data
        sendGLData( m_mesh.vertices(), VERTEX_POSITION );
        sendGLData( m_mesh.normals(), VERTEX_NORMAL );

        // Vec3 data

        if ( m_v3DataHandle[VERTEX_TANGENT].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_TANGENT] ).data(),
                        MAX_MESH + VERTEX_TANGENT );
        if ( m_v3DataHandle[VERTEX_BITANGENT].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_BITANGENT] ).data(),
                        MAX_MESH + VERTEX_BITANGENT );
        if ( m_v3DataHandle[VERTEX_TEXCOORD].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v3DataHandle[VERTEX_TEXCOORD] ).data(),
                        MAX_MESH + VERTEX_TEXCOORD );

        // Vec4 data
        if ( m_v4DataHandle[VERTEX_COLOR].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_COLOR] ).data(),
                        MAX_MESH + MAX_VEC3 + VERTEX_COLOR );

        if ( m_v4DataHandle[VERTEX_WEIGHTS].isValid() )
            sendGLData( m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_WEIGHTS] ).data(),
                        MAX_MESH + MAX_VEC3 + VERTEX_WEIGHTS );

        if ( m_v4DataHandle[VERTEX_WEIGHT_IDX].isValid() )
            sendGLData(
                m_mesh.attribManager().getAttrib( m_v4DataHandle[VERTEX_WEIGHT_IDX] ).data(),
                MAX_MESH + MAX_VEC3 + VERTEX_WEIGHT_IDX );
        GL_ASSERT( glBindVertexArray( 0 ) );
        GL_CHECK_ERROR;
        m_isDirty = false;
    }
}

} // namespace Engine
} // namespace Ra
<|endoftext|>


<commit_before>#include "Firmware.h"
#include <avr/eeprom.h>
#include <avr/pgmspace.h>

#define VERSION_POINT_LOCATION  0x67F6
#define CRC_LOCATION_FLASH      0x67FE
#define CRC_LOCATION_EEPROM     (EEPROM_SIZE - 2)   //write crc to last eeprom location

bool checkNewRevision() {

    //current app crc is written to last flash location
    //previous crc is stored into eeprom
    //if two differ, app has changed

    uint16_t crc_eeprom = eeprom_read_word((uint16_t*)CRC_LOCATION_EEPROM);
    uint16_t crc_flash = pgm_read_word_near(CRC_LOCATION_FLASH);

    if (crc_eeprom != crc_flash)   {

        eeprom_update_word((uint16_t*)CRC_LOCATION_EEPROM, crc_flash);
        return true;

    }   return false;

    return false;

}

uint8_t getSWversion(swVersion_t point) {

    switch(point)   {

        case swVersion_major:
        case swVersion_minor:
        case swVersion_revision:
        case swVersion_development:
        return (uint8_t)pgm_read_word_far(VERSION_POINT_LOCATION+(uint8_t)point*2);

        default:
        return 0;

    }

}<commit_msg>use addresses specified as symbols<commit_after>#include "Firmware.h"
#include <avr/eeprom.h>
#include <avr/pgmspace.h>

#define VERSION_POINT_LOCATION  (FLASH_SIZE - 10) //8 bytes for version, 2 bytes for crc
#define CRC_LOCATION_FLASH      (FLASH_SIZE - 2)
#define CRC_LOCATION_EEPROM     (EEPROM_SIZE - 2)   //write crc to last eeprom location

//bool checkNewRevision() {
//
    ////current app crc is written to last flash location
    ////previous crc is stored into eeprom
    ////if two differ, app has changed
//
    //uint16_t crc_eeprom = eeprom_read_word((uint16_t*)CRC_LOCATION_EEPROM);
    //uint16_t crc_flash = pgm_read_word_near(CRC_LOCATION_FLASH);
//
    //if (crc_eeprom != crc_flash)   {
//
        //eeprom_update_word((uint16_t*)CRC_LOCATION_EEPROM, crc_flash);
        //return true;
//
    //}   return false;
//
    //return false;
//
//}

uint8_t getSWversion(swVersion_t point) {

    switch(point)   {

        case swVersion_major:
        case swVersion_minor:
        case swVersion_revision:
        case swVersion_development:
        return (uint8_t)pgm_read_word_far(VERSION_POINT_LOCATION+(uint8_t)point*2);

        default:
        return 0;

    }

}<|endoftext|>