197 lines
5.3 KiB
C++
197 lines
5.3 KiB
C++
#include "stdafx.h"
|
|
#include "RuntimeParameterUtils.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cctype>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
std::string TrimText(const std::string& text)
|
|
{
|
|
std::size_t start = 0;
|
|
while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start])))
|
|
++start;
|
|
|
|
std::size_t end = text.size();
|
|
while (end > start && std::isspace(static_cast<unsigned char>(text[end - 1])))
|
|
--end;
|
|
|
|
return text.substr(start, end - start);
|
|
}
|
|
|
|
bool IsFiniteNumber(double value)
|
|
{
|
|
return std::isfinite(value) != 0;
|
|
}
|
|
|
|
std::vector<double> JsonArrayToNumbers(const JsonValue& value)
|
|
{
|
|
std::vector<double> numbers;
|
|
for (const JsonValue& item : value.asArray())
|
|
{
|
|
if (item.isNumber())
|
|
numbers.push_back(item.asNumber());
|
|
}
|
|
return numbers;
|
|
}
|
|
|
|
std::string NormalizeTextValue(const std::string& text, unsigned maxLength)
|
|
{
|
|
std::string normalized;
|
|
normalized.reserve(std::min<std::size_t>(text.size(), maxLength));
|
|
for (unsigned char ch : text)
|
|
{
|
|
if (ch < 32 || ch > 126)
|
|
continue;
|
|
if (normalized.size() >= maxLength)
|
|
break;
|
|
normalized.push_back(static_cast<char>(ch));
|
|
}
|
|
return normalized;
|
|
}
|
|
}
|
|
|
|
std::string MakeSafePresetFileStem(const std::string& presetName)
|
|
{
|
|
std::string trimmed = TrimText(presetName);
|
|
std::string safe;
|
|
safe.reserve(trimmed.size());
|
|
|
|
for (unsigned char ch : trimmed)
|
|
{
|
|
if (std::isalnum(ch))
|
|
safe.push_back(static_cast<char>(std::tolower(ch)));
|
|
else if (ch == ' ' || ch == '-' || ch == '_')
|
|
{
|
|
if (safe.empty() || safe.back() == '-')
|
|
continue;
|
|
safe.push_back('-');
|
|
}
|
|
}
|
|
|
|
while (!safe.empty() && safe.back() == '-')
|
|
safe.pop_back();
|
|
|
|
return safe;
|
|
}
|
|
|
|
ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition)
|
|
{
|
|
ShaderParameterValue value;
|
|
switch (definition.type)
|
|
{
|
|
case ShaderParameterType::Float:
|
|
value.numberValues = definition.defaultNumbers.empty() ? std::vector<double>{ 0.0 } : definition.defaultNumbers;
|
|
break;
|
|
case ShaderParameterType::Vec2:
|
|
value.numberValues = definition.defaultNumbers.size() == 2 ? definition.defaultNumbers : std::vector<double>{ 0.0, 0.0 };
|
|
break;
|
|
case ShaderParameterType::Color:
|
|
value.numberValues = definition.defaultNumbers.size() == 4 ? definition.defaultNumbers : std::vector<double>{ 1.0, 1.0, 1.0, 1.0 };
|
|
break;
|
|
case ShaderParameterType::Boolean:
|
|
value.booleanValue = definition.defaultBoolean;
|
|
break;
|
|
case ShaderParameterType::Enum:
|
|
value.enumValue = definition.defaultEnumValue;
|
|
break;
|
|
case ShaderParameterType::Text:
|
|
value.textValue = NormalizeTextValue(definition.defaultTextValue, definition.maxLength);
|
|
break;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
bool NormalizeAndValidateParameterValue(const ShaderParameterDefinition& definition, const JsonValue& value, ShaderParameterValue& normalizedValue, std::string& error)
|
|
{
|
|
normalizedValue = DefaultValueForDefinition(definition);
|
|
|
|
switch (definition.type)
|
|
{
|
|
case ShaderParameterType::Float:
|
|
{
|
|
if (!value.isNumber())
|
|
{
|
|
error = "Expected numeric value for float parameter '" + definition.id + "'.";
|
|
return false;
|
|
}
|
|
double number = value.asNumber();
|
|
if (!IsFiniteNumber(number))
|
|
{
|
|
error = "Float parameter '" + definition.id + "' must be finite.";
|
|
return false;
|
|
}
|
|
if (!definition.minNumbers.empty())
|
|
number = std::max(number, definition.minNumbers.front());
|
|
if (!definition.maxNumbers.empty())
|
|
number = std::min(number, definition.maxNumbers.front());
|
|
normalizedValue.numberValues = { number };
|
|
return true;
|
|
}
|
|
case ShaderParameterType::Vec2:
|
|
case ShaderParameterType::Color:
|
|
{
|
|
std::vector<double> numbers = JsonArrayToNumbers(value);
|
|
const std::size_t expectedSize = definition.type == ShaderParameterType::Vec2 ? 2 : 4;
|
|
if (numbers.size() != expectedSize)
|
|
{
|
|
error = "Expected array value of size " + std::to_string(expectedSize) + " for parameter '" + definition.id + "'.";
|
|
return false;
|
|
}
|
|
for (std::size_t index = 0; index < numbers.size(); ++index)
|
|
{
|
|
if (!IsFiniteNumber(numbers[index]))
|
|
{
|
|
error = "Parameter '" + definition.id + "' contains a non-finite value.";
|
|
return false;
|
|
}
|
|
if (index < definition.minNumbers.size())
|
|
numbers[index] = std::max(numbers[index], definition.minNumbers[index]);
|
|
if (index < definition.maxNumbers.size())
|
|
numbers[index] = std::min(numbers[index], definition.maxNumbers[index]);
|
|
}
|
|
normalizedValue.numberValues = numbers;
|
|
return true;
|
|
}
|
|
case ShaderParameterType::Boolean:
|
|
if (!value.isBoolean())
|
|
{
|
|
error = "Expected boolean value for parameter '" + definition.id + "'.";
|
|
return false;
|
|
}
|
|
normalizedValue.booleanValue = value.asBoolean();
|
|
return true;
|
|
case ShaderParameterType::Enum:
|
|
{
|
|
if (!value.isString())
|
|
{
|
|
error = "Expected string value for enum parameter '" + definition.id + "'.";
|
|
return false;
|
|
}
|
|
const std::string selectedValue = value.asString();
|
|
for (const ShaderParameterOption& option : definition.enumOptions)
|
|
{
|
|
if (option.value == selectedValue)
|
|
{
|
|
normalizedValue.enumValue = selectedValue;
|
|
return true;
|
|
}
|
|
}
|
|
error = "Enum parameter '" + definition.id + "' received unsupported option '" + selectedValue + "'.";
|
|
return false;
|
|
}
|
|
case ShaderParameterType::Text:
|
|
if (!value.isString())
|
|
{
|
|
error = "Expected string value for text parameter '" + definition.id + "'.";
|
|
return false;
|
|
}
|
|
normalizedValue.textValue = NormalizeTextValue(value.asString(), definition.maxLength);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|