TArray<FString> UCurveBase::CreateCurveFromCSVString(const FString& InString) { // Array used to store problems about curve import TArray<FString> OutProblems; TArray<FRichCurveEditInfo> Curves = GetCurves(); const int32 NumCurves = Curves.Num(); const FCsvParser Parser(InString); const FCsvParser::FRows& Rows = Parser.GetRows(); if(Rows.Num() == 0) { OutProblems.Add(FString(TEXT("No data."))); return OutProblems; } // First clear out old data. ResetCurve(); // Each row represents a point for(int32 RowIdx=0; RowIdx<Rows.Num(); RowIdx++) { const TArray<const TCHAR*>& Cells = Rows[RowIdx]; const int32 NumCells = Cells.Num(); // Need at least two cell, Time and one Value if(NumCells < 2) { OutProblems.Add(FString::Printf(TEXT("Row '%d' has less than 2 cells."), RowIdx)); continue; } float Time = FCString::Atof(Cells[0]); for(int32 CellIdx=1; CellIdx<NumCells && CellIdx<(NumCurves+1); CellIdx++) { FRichCurve* Curve = Curves[CellIdx-1].CurveToEdit; if(Curve != NULL) { FKeyHandle KeyHandle = Curve->AddKey(Time, FCString::Atof(Cells[CellIdx])); Curve->SetKeyInterpMode(KeyHandle, RCIM_Linear); } } // If we get more cells than curves (+1 for time cell) if(NumCells > (NumCurves + 1)) { OutProblems.Add(FString::Printf(TEXT("Row '%d' has too many cells for the curve(s)."), RowIdx)); } // If we got too few cells else if(NumCells < (NumCurves + 1)) { OutProblems.Add(FString::Printf(TEXT("Row '%d' has too few cells for the curve(s)."), RowIdx)); } } Modify(true); return OutProblems; }
TArray<FString> UCurveTable::CreateTableFromJSONString(const FString& InString, ERichCurveInterpMode InterpMode) { // Array used to store problems about table creation TArray<FString> OutProblems; if (InString.IsEmpty()) { OutProblems.Add(TEXT("Input data is empty.")); return OutProblems; } TArray< TSharedPtr<FJsonValue> > ParsedTableRows; { const TSharedRef< TJsonReader<TCHAR> > JsonReader = TJsonReaderFactory<TCHAR>::Create(InString); if (!FJsonSerializer::Deserialize(JsonReader, ParsedTableRows) || ParsedTableRows.Num() == 0) { OutProblems.Add(FString::Printf(TEXT("Failed to parse the JSON data. Error: %s"), *JsonReader->GetErrorMessage())); return OutProblems; } } // Empty existing data EmptyTable(); // Iterate over rows for (int32 RowIdx = 0; RowIdx < ParsedTableRows.Num(); ++RowIdx) { const TSharedPtr<FJsonValue>& ParsedTableRowValue = ParsedTableRows[RowIdx]; TSharedPtr<FJsonObject> ParsedTableRowObject = ParsedTableRowValue->AsObject(); if (!ParsedTableRowObject.IsValid()) { OutProblems.Add(FString::Printf(TEXT("Row '%d' is not a valid JSON object."), RowIdx)); continue; } // Get row name static const FString RowNameJsonKey = TEXT("Name"); const FName RowName = MakeValidName(ParsedTableRowObject->GetStringField(RowNameJsonKey)); // Check its not 'none' if (RowName == NAME_None) { OutProblems.Add(FString::Printf(TEXT("Row '%d' missing a name."), RowIdx)); continue; } // Check its not a duplicate if (RowMap.Find(RowName) != NULL) { OutProblems.Add(FString::Printf(TEXT("Duplicate row name '%s'."), *RowName.ToString())); continue; } // Add a key for each entry in this row FRichCurve* NewCurve = new FRichCurve(); for (const auto& ParsedTableRowEntry : ParsedTableRowObject->Values) { // Skip the name entry if (ParsedTableRowEntry.Key == RowNameJsonKey) { continue; } // Make sure we have a valid float key float EntryKey = 0.0f; if (!LexicalConversion::TryParseString(EntryKey, *ParsedTableRowEntry.Key)) { OutProblems.Add(FString::Printf(TEXT("Key '%s' on row '%s' is not a float and cannot be parsed."), *ParsedTableRowEntry.Key, *RowName.ToString())); continue; } // Make sure we have a valid float value double EntryValue = 0.0; if (!ParsedTableRowEntry.Value->TryGetNumber(EntryValue)) { OutProblems.Add(FString::Printf(TEXT("Entry '%s' on row '%s' is not a float and cannot be parsed."), *ParsedTableRowEntry.Key, *RowName.ToString())); continue; } FKeyHandle KeyHandle = NewCurve->AddKey(EntryKey, static_cast<float>(EntryValue)); NewCurve->SetKeyInterpMode(KeyHandle, InterpMode); } RowMap.Add(RowName, NewCurve); } Modify(true); return OutProblems; }
TArray<FString> UCurveTable::CreateTableFromCSVString(const FString& InString, ERichCurveInterpMode InterpMode) { // Array used to store problems about table creation TArray<FString> OutProblems; const FCsvParser Parser(InString); const FCsvParser::FRows& Rows = Parser.GetRows(); // Must have at least 2 rows (x values + y values for at least one row) if(Rows.Num() <= 1) { OutProblems.Add(FString(TEXT("Too few rows."))); return OutProblems; } // Empty existing data EmptyTable(); TArray<float> XValues; GetCurveValues(Rows[0], &XValues); // Iterate over rows for(int32 RowIdx = 1; RowIdx < Rows.Num(); RowIdx++) { const TArray<const TCHAR*>& Row = Rows[RowIdx]; // Need at least 1 cells (row name) if(Row.Num() < 1) { OutProblems.Add(FString::Printf(TEXT("Row '%d' has too few cells."), RowIdx)); continue; } // Get row name FName RowName = MakeValidName(Row[0]); // Check its not 'none' if(RowName == NAME_None) { OutProblems.Add(FString::Printf(TEXT("Row '%d' missing a name."), RowIdx)); continue; } // Check its not a duplicate if(RowMap.Find(RowName) != NULL) { OutProblems.Add(FString::Printf(TEXT("Duplicate row name '%s'."), *RowName.ToString())); continue; } TArray<float> YValues; GetCurveValues(Row, &YValues); if(XValues.Num() != YValues.Num()) { OutProblems.Add(FString::Printf(TEXT("Row '%s' does not have the right number of columns."), *RowName.ToString())); continue; } FRichCurve* NewCurve = new FRichCurve(); // Now iterate over cells (skipping first cell, that was row name) for(int32 ColumnIdx = 0; ColumnIdx < XValues.Num(); ColumnIdx++) { FKeyHandle KeyHandle = NewCurve->AddKey(XValues[ColumnIdx], YValues[ColumnIdx]); NewCurve->SetKeyInterpMode(KeyHandle, InterpMode); } RowMap.Add(RowName, NewCurve); } Modify(true); return OutProblems; }