csgo-2018-source/movieobjects/dmeexpressionoperator.cpp
2021-07-24 21:11:47 -07:00

188 lines
5.4 KiB
C++

//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
//
// The expression operator class - scalar math calculator
// for a good list of operators and simple functions, see:
// \\fileserver\user\MarcS\boxweb\aliveDistLite\v4.2.0\doc\alive\functions.txt
// (although we'll want to implement elerp as the standard 3x^2 - 2x^3 with rescale)
//
//=============================================================================
#include "movieobjects/dmeexpressionoperator.h"
#include "movieobjects_interfaces.h"
#include "datamodel/dmelementfactoryhelper.h"
#include "datamodel/dmattribute.h"
#include "mathlib/noise.h"
#include "mathlib/vector.h"
#include <ctype.h>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void TestCalculator( const char *expr, float answer )
{
CExpressionCalculator calc( expr );
float result = 0.0f;
#ifdef _DEBUG
bool success =
#endif
calc.Evaluate( result );
Assert( success && ( result == answer ) );
}
void TestCalculator( const char *expr, float answer, const char *var, float value )
{
CExpressionCalculator calc( expr );
calc.SetVariable( var, value );
float result = 0.0f;
#ifdef _DEBUG
bool success =
#endif
calc.Evaluate( result );
Assert( success && ( result == answer ) );
}
void TestCalculator()
{
// TestCalculator( "-1", 1 );
TestCalculator( "2 * 3 + 4", 10 );
TestCalculator( "2 + 3 * 4", 14 );
TestCalculator( "2 * 3 * 4", 24 );
TestCalculator( "2 * -3 + 4", -2 );
TestCalculator( "12.0 / 2.0", 6 );
TestCalculator( "(2*3)+4", 10 );
TestCalculator( "( 1 + 2 ) / (1+2)", 1 );
TestCalculator( "(((5)))", 5 );
TestCalculator( "--5", 5 );
TestCalculator( "3.5 % 2", 1.5 );
TestCalculator( "1e-2", 0.01 );
TestCalculator( "9 == ( 3 * ( 1 + 2 ) )", 1 );
TestCalculator( "9 != ( 3 * ( 1 + 2 ) )", 0 );
TestCalculator( "9 <= ( 3 * ( 1 + 2 ) )", 1 );
TestCalculator( "9 < ( 3 * ( 1 + 2 ) )", 0 );
TestCalculator( "9 < 3", 0 );
TestCalculator( "10 >= 5", 1 );
// TestCalculator( "9 < ( 3 * ( 2 + 2 ) )", 0 );
TestCalculator( "x + 1", 5, "x", 4 );
TestCalculator( "pi - 3.14159", 0, "pi", 3.14159 );
// TestCalculator( "pi / 2", 0, "pi", 3.14159 );
TestCalculator( "abs(-10)", 10 );
TestCalculator( "sqr(-5)", 25 );
TestCalculator( "sqrt(9)", 3 );
// TestCalculator( "sqrt(-9)", -3 );
TestCalculator( "pow(2,3)", 8 );
TestCalculator( "min(abs(-4),2+3/2)", 3.5 );
TestCalculator( "round(0.5)", 1 );
TestCalculator( "round(0.49)", 0 );
TestCalculator( "round(-0.5)", 0 );
TestCalculator( "round(-0.51)", -1 );
TestCalculator( "inrange( 5, -8, 10 )", 1 );
TestCalculator( "inrange( 5, 5, 10 )", 1 );
TestCalculator( "inrange( 5, 6, 10 )", 0 );
TestCalculator( "elerp( 1/4, 0, 1 )", 3/16.0f - 1/32.0f );
TestCalculator( "rescale( 0.5, -1, 1, 0, 100 )", 75 );
TestCalculator( "1 > 2 ? 6 : 9", 9 );
TestCalculator( "1 ? 1 ? 2 : 4 : 1 ? 6 : 8", 2 );
TestCalculator( "0 ? 1 ? 2 : 4 : 1 ? 6 : 8", 6 );
TestCalculator( "noise( 0.123, 4.56, 78.9 )", ImprovedPerlinNoise( Vector( 0.123, 4.56, 78.9 ) ) );
}
//-----------------------------------------------------------------------------
// Expose this class to the scene database
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeExpressionOperator, CDmeExpressionOperator );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CDmeExpressionOperator::OnConstruction()
{
m_result.Init( this, "result" );
m_expr.Init( this, "expr" );
m_bSpewResult.Init( this, "spewresult" );
#ifdef _DEBUG
TestCalculator();
#endif _DEBUG
}
void CDmeExpressionOperator::OnDestruction()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CDmeExpressionOperator::IsInputAttribute( CDmAttribute *pAttribute )
{
const char *pName = pAttribute->GetName( );
#if 0 // skip this test, since none of these are float attributes, but leave the code as a reminder
if ( Q_strcmp( pName, "name" ) == 0 )
return false;
if ( Q_strcmp( pName, "expr" ) == 0 )
return false;
#endif
if ( Q_strcmp( pName, "result" ) == 0 )
return false;
if ( pAttribute->GetType() != AT_FLOAT )
return false;
return true;
}
void CDmeExpressionOperator::Operate()
{
m_calc.SetExpression( m_expr.Get() );
for ( CDmAttribute *pAttribute = FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
{
if ( IsInputAttribute( pAttribute ) )
{
const char *pName = pAttribute->GetName( );
m_calc.SetVariable( pName, pAttribute->GetValue< float >() );
}
}
float oldValue = m_result;
m_calc.Evaluate( oldValue );
m_result = oldValue;
if ( m_bSpewResult )
{
Msg( "%s = '%f'\n", GetName(), (float)m_result );
}
}
void CDmeExpressionOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
for ( CDmAttribute *pAttribute = FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
{
if ( IsInputAttribute( pAttribute ) )
{
attrs.AddToTail( pAttribute );
}
}
attrs.AddToTail( m_expr.GetAttribute() );
}
void CDmeExpressionOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
{
attrs.AddToTail( m_result.GetAttribute() );
}
void CDmeExpressionOperator::SetSpewResult( bool state )
{
m_bSpewResult = state;
}