/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2021-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM 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. OpenFOAM 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 OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "gltfCoordSetWriter.H" #include "coordSet.H" #include "fileName.H" #include "OFstream.H" #include "OSspecific.H" #include "foamGltfScene.H" #include "foamGltfSceneWriter.H" #include "coordSetWriterMethods.H" #include "addToRunTimeSelectionTable.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { namespace coordSetWriters { defineTypeName(gltfWriter); addToRunTimeSelectionTable(coordSetWriter, gltfWriter, word); addToRunTimeSelectionTable(coordSetWriter, gltfWriter, wordDict); } } const Foam::Enum Foam::coordSetWriters::gltfWriter::fieldOptionNames_ ({ // No naming for NONE { fieldOption::UNIFORM, "uniform" }, { fieldOption::FIELD, "field" }, }); // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // namespace Foam { template static tmp getBoundedColours ( const colourTable& colours, const Field& field, const scalar boundMin, const scalar boundMax ) { const scalar boundDelta = (boundMax - boundMin + ROOTVSMALL); auto tresult = tmp::New(field.size()); auto& result = tresult.ref(); forAll(field, i) { const Type& val = field[i]; const scalar f = ( pTraits::nComponents == 1 ? scalar(component(val, 0)) : scalar(mag(val)) ); // 0-1 clipped by value() result[i] = colours.value((f - boundMin)/boundDelta); } return tresult; } template static vector getAnimationColour ( const dictionary& dict, const colourTable& colours, const Field& field ) { scalar refValue(0); scalarMinMax valLimits; if (pTraits::nComponents == 1) { MinMax scanned(minMax(field)); refValue = scalar(component(field[0], 0)); valLimits.min() = scalar(component(scanned.min(), 0)); valLimits.max() = scalar(component(scanned.max(), 0)); } else { // Use mag() for multiple components refValue = mag(field[0]); valLimits = minMaxMag(field); } dict.readIfPresent("min", valLimits.min()); dict.readIfPresent("max", valLimits.max()); const scalar fraction = ( (refValue - valLimits.min()) / (valLimits.max() - valLimits.min() + ROOTVSMALL) ); // 0-1 clipped by value() return colours.value(fraction); } } // End namespace Foam // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // Foam::word Foam::coordSetWriters::gltfWriter::getColourMap ( const dictionary& dict ) const { word colourMap = colourTable::predefinedNames.names()[0]; dict.readIfPresent("colourMap", colourMap); return colourMap; } const Foam::colourTable& Foam::coordSetWriters::gltfWriter::getColourTable ( const dictionary& dict ) const { return colourTable::ref(getColourMap(dict)); } Foam::scalarMinMax Foam::coordSetWriters::gltfWriter::getFieldLimits ( const word& fieldName ) const { const dictionary fieldDict = fieldInfoDict_.subOrEmptyDict(fieldName); scalarMinMax limits(-GREAT, GREAT); fieldDict.readIfPresent("min", limits.min()); fieldDict.readIfPresent("max", limits.max()); return limits; } Foam::tmp Foam::coordSetWriters::gltfWriter::getAlphaField ( const dictionary& dict ) const { // Fallback value scalar alphaValue(1); const entry* eptr = dict.findEntry("alpha", keyType::LITERAL); if (!eptr) { // Not specified } else if (!eptr->stream().peek().isWord()) { // Value specified ITstream& is = eptr->stream(); is >> alphaValue; dict.checkITstream(is, "alpha"); } else { // Enumeration const auto option = fieldOptionNames_.get("alpha", dict); switch (option) { case fieldOption::NONE: { break; } case fieldOption::UNIFORM: { dict.readEntry("alphaValue", alphaValue); break; } case fieldOption::FIELD: { WarningInFunction << "Unsupported 'field' specification for alpha values" << endl; break; } } } return tmp::New(1, alphaValue); } void Foam::coordSetWriters::gltfWriter::setupAnimationColour() { const dictionary& dict = animationDict_; const entry* eptr = dict.findEntry("colour", keyType::LITERAL); if (!eptr || !eptr->isStream()) { FatalIOErrorInFunction(dict) << "Missing 'colour' entry" << exit(FatalIOError); } else if (!eptr->stream().peek().isString()) { // Value specified ITstream& is = eptr->stream(); is >> animateColourValue_; dict.checkITstream(is, "colour"); // Has uniform value animateColourOption_ = fieldOption::UNIFORM; } else { // Enumeration const auto option = fieldOptionNames_.get("colour", dict); switch (option) { case fieldOption::NONE: { FatalErrorInFunction << "Cannot select 'none' for colour entry!" << nl << "... possible programming error" << exit(FatalError); break; } case fieldOption::UNIFORM: { dict.readEntry("colourValue", animateColourValue_); // Has uniform value animateColourOption_ = fieldOption::UNIFORM; break; } case fieldOption::FIELD: { // Needs named field... animateColourName_ = dict.get("colourField"); animateColourOption_ = fieldOption::FIELD; break; } } } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::coordSetWriters::gltfWriter::gltfWriter() : coordSetWriter(), writer_(nullptr), animate_(false), colour_(false), animateColourOption_(fieldOption::NONE), animateColourName_(), animateColourValue_(Zero), fieldInfoDict_(), animationDict_() {} Foam::coordSetWriters::gltfWriter::gltfWriter(const dictionary& options) : coordSetWriter(options), writer_(nullptr), animate_(options.getOrDefault("animate", false)), colour_(options.getOrDefault("colour", false)), animateColourOption_(fieldOption::NONE), animateColourName_(), animateColourValue_(Zero), fieldInfoDict_(options.subOrEmptyDict("fieldInfo")), animationDict_(options.subOrEmptyDict("animationInfo")) { // fieldInfo // { // U // { // colourMap coolToWarm; // others... // min 10; // max 100; // alpha 0.5; // } // } } Foam::coordSetWriters::gltfWriter::gltfWriter ( const coordSet& coords, const fileName& outputPath, const dictionary& options ) : gltfWriter(options) { open(coords, outputPath); } Foam::coordSetWriters::gltfWriter::gltfWriter ( const UPtrList& tracks, const fileName& outputPath, const dictionary& options ) : gltfWriter(options) { open(tracks, outputPath); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::coordSetWriters::gltfWriter::~gltfWriter() { close(); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::fileName Foam::coordSetWriters::gltfWriter::path() const { // 1) rootdir/