Files
projectm/src/libprojectM/MilkdropPresetFactory/Expr.cpp
deltaoscarmike 2ce0e70a98 memleaks fixes
2018-06-10 22:02:06 +02:00

537 lines
13 KiB
C++
Executable File

/**
* projectM -- Milkdrop-esque visualisation SDK
* Copyright (C)2003-2004 projectM Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
#include "wipemalloc.h"
#include "Expr.hpp"
#include <cassert>
#include <iostream>
#include "Eval.hpp"
/* Evaluates functions in prefix form */
float PrefunExpr::eval ( int mesh_i, int mesh_j )
{
assert ( func_ptr );
float arg_list_stk[10];
float * arg_list;
float * argp;
Expr **expr_listp = expr_list;
if (this->num_args > 10) {
arg_list = new float[this->num_args];
} else {
arg_list = arg_list_stk;
}
argp = arg_list;
assert(arg_list);
//printf("numargs %d", num_args);
/* Evaluate each argument before calling the function itself */
for ( int i = 0; i < num_args; i++ )
{
*(argp++) = (*(expr_listp++))->eval ( mesh_i, mesh_j );
//printf("numargs %x", arg_list[i]);
}
/* Now we call the function, passing a list of
floats as its argument */
const float value = ( func_ptr ) ( arg_list );
if (arg_list != arg_list_stk) {
delete[](arg_list);
}
return value;
}
class PrefunExprOne : public PrefunExpr
{
float eval ( int mesh_i, int mesh_j )
{
float val = expr_list[0]->eval ( mesh_i, mesh_j );
return (func_ptr)(&val);
}
};
class ConstantExpr : public Expr
{
float constant;
public:
ConstantExpr( float value ) : Expr(CONSTANT), constant(value) {}
ConstantExpr( int type, Term *term ) : Expr(CONSTANT), constant(term->constant) {}
bool isConstant()
{
return true;
}
float eval(int mesh_i, int mesh_j )
{
return constant;
}
std::ostream &to_string(std::ostream &out)
{
out << constant; return out;
}
};
class ParameterExpr : public Expr
{
protected:
Term term;
public:
ParameterExpr( int _type, Term *_term ) : Expr(PARAMETER), term(*_term) {}
float eval(int mesh_i, int mesh_j ) = 0;
std::ostream& to_string(std::ostream& out)
{
out << term.param->name;
return out;
}
};
class BoolParameterExpr : public ParameterExpr
{
public:
BoolParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_BOOL );
return ( float ) ( * ( ( bool* ) ( term.param->engine_val ) ) );
}
};
class IntParameterExpr : public ParameterExpr
{
public:
IntParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_INT );
return ( float ) ( * ( ( int* ) ( term.param->engine_val ) ) );
}
};
class FloatParameterExpr : public ParameterExpr
{
public:
FloatParameterExpr( int _type, Term *_term ) : ParameterExpr(_type,_term) {}
float eval ( int mesh_i, int mesh_j );
};
/* TODO optimize FloatParameterExpr::eval() further.
// - flag "never matrix" parameters
// - always pass in 2d matrix. instead of using (i, -1) for 1d matrix, we could just use (0, i) and avoid check
// - instead of using matrix_flag to give "copy on write" behavior, maybe pre-copy from engine_val to matrix[]
*/
float FloatParameterExpr::eval ( int mesh_i, int mesh_j )
{
assert( term.param->type == P_TYPE_DOUBLE );
if ( term.param->matrix_flag | ( term.param->flags & P_FLAG_ALWAYS_MATRIX ) )
{
/* Sanity check the matrix is there... */
assert ( term.param->matrix != NULL );
/// @slow boolean check could be expensive in this critical (and common) step of evaluation
if ( mesh_i >= 0 )
{
if ( mesh_j >= 0 )
{
return ( ( ( float** ) term.param->matrix ) [mesh_i][mesh_j] );
}
else
{
// issue 64, make sure we're not reading a P_FLAG_PER_PIXEL matrix variable
if (!(term.param->flags & P_FLAG_PER_PIXEL))
return ( ( ( float* ) term.param->matrix ) [mesh_i] );
}
}
//assert(mesh_i >=0);
}
//std::cout << term.param->name << ": " << (*((float*)term.param->engine_val)) << std::endl;
return * ( ( float* ) ( term.param->engine_val ) );
}
class MultAndAddExpr : public Expr
{
Expr *a, *b, *c;
public:
MultAndAddExpr(Expr *_a, Expr *_b, Expr *_c) : Expr(OTHER),
a(_a), b(_b), c(_c)
{
}
~MultAndAddExpr() {
delete a;
delete b;
delete c;
}
float eval(int mesh_i, int mesh_j)
{
float a_value = a->eval(mesh_i,mesh_j);
float b_value = b->eval(mesh_i,mesh_j);
float c_value = c->eval(mesh_i,mesh_j);
return a_value * b_value + c_value;
}
std::ostream &to_string(std::ostream &out)
{
out << "(" << a << " * " << b << ") + " << c;
return out;
}
};
std::ostream &TreeExpr::to_string(std::ostream &out)
{
if (NULL == infix_op)
{
out << gen_expr;
}
else
{
out << "(" << left << " ";
switch ( infix_op->type )
{
case INFIX_ADD:
out << "+"; break;
case INFIX_MINUS:
out << "-"; break;
case INFIX_MULT:
out << "*"; break;
case INFIX_MOD:
out << "%"; break;
case INFIX_OR:
out << "|"; break;
case INFIX_AND:
out << "&"; break;
case INFIX_DIV:
out << "/"; break;
default:
out << "infix_op_ERROR"; break;
}
out << " " << right << ")";
}
return out;
}
/* NOTE: Parser.cpp directly manipulates TreeExpr, so it is easier to optimizer AFTER parsing
* than while building up the tree initially
*/
Expr *TreeExpr::optimize()
{
if (infix_op == NULL)
{
Expr *opt = gen_expr->optimize();
if (opt != gen_expr)
delete gen_expr;
gen_expr = NULL;
return opt;
}
if (left != NULL)
{
Expr *l = left->optimize();
if (l != left)
delete left;
left = l;
}
if (right != NULL)
{
Expr *r = right->optimize();
if (r != right)
delete right;
right = r;
}
if (left == NULL)
{
Expr *opt = right;
right = NULL;
return opt;
}
if (right == NULL)
{
Expr *opt = left;
left = NULL;
return opt;
}
if (left->isConstant() && right->isConstant())
return Expr::const_to_expr(eval(-1, -1));
// this is gratuitious, but a*b+c is super common, so as proof-of-concept, let's make a special Expr
if (infix_op->type == INFIX_ADD &&
((left->clazz == TREE && ((TreeExpr *)left)->infix_op->type == INFIX_MULT) ||
(right->clazz == TREE && ((TreeExpr *)right)->infix_op->type == INFIX_MULT)))
{
Expr *a, *b, *c;
if (left->clazz == TREE && ((TreeExpr *)left)->infix_op->type == INFIX_MULT)
{
a = ((TreeExpr *)left)->left;
b = ((TreeExpr *)left)->right;
c = right;
((TreeExpr *)left)->left = NULL;
((TreeExpr *)left)->right = NULL;
right = NULL;
}
else
{
a = ((TreeExpr *)right)->left;
b = ((TreeExpr *)right)->right;
c = left;
((TreeExpr *)right)->left = NULL;
((TreeExpr *)right)->right = NULL;
left = NULL;
}
return new MultAndAddExpr(a,b,c);
}
return this;
}
/* Evaluates an expression tree */
float TreeExpr::eval ( int mesh_i, int mesh_j )
{
float left_arg, right_arg;
/* shouldn't be null if we've called optimize() */
assert(NULL != infix_op);
left_arg = left->eval ( mesh_i, mesh_j );
right_arg = right->eval ( mesh_i, mesh_j );
switch ( infix_op->type )
{
case INFIX_ADD:
return ( left_arg + right_arg );
case INFIX_MINUS:
return ( left_arg - right_arg );
case INFIX_MULT:
return ( left_arg * right_arg );
case INFIX_MOD:
if ( ( int ) right_arg == 0 )
{
return PROJECTM_DIV_BY_ZERO;
}
return ( ( int ) left_arg % ( int ) right_arg );
case INFIX_OR:
return ( ( int ) left_arg | ( int ) right_arg );
case INFIX_AND:
return ( ( int ) left_arg & ( int ) right_arg );
case INFIX_DIV:
if ( right_arg == 0 )
{
return MAX_DOUBLE_SIZE;
}
return ( left_arg / right_arg );
default:
return EVAL_ERROR;
}
return EVAL_ERROR;
}
/* Converts a float value to a general expression */
Expr * Expr::const_to_expr ( float val )
{
Term term;
term.constant = val;
return new ConstantExpr( CONSTANT_TERM_T, &term );
}
/* Converts a regular parameter to an expression */
Expr * Expr::param_to_expr ( Param * param )
{
Term term;
if ( param == NULL )
return NULL;
/* This code is still a work in progress. We need
to figure out if the initial condition is used for
each per frame equation or not. I am guessing that
it isn't, and it is thusly implemented this way */
/* Current guess of true behavior (08/01/03) note from carm
First try to use the per_pixel_expr (with cloning)
If it is null however, use the engine variable instead. */
/* 08/20/03 : Presets are now objects, as well as per pixel equations. This ends up
making the parser handle the case where parameters are essentially per pixel equation
substitutions */
term.param = param;
switch ( param->type )
{
case P_TYPE_BOOL:
return new BoolParameterExpr( PARAM_TERM_T, &term );
case P_TYPE_INT:
return new IntParameterExpr( PARAM_TERM_T, &term );
case P_TYPE_DOUBLE:
return new FloatParameterExpr( PARAM_TERM_T, &term );
}
return NULL;
}
/* Converts a prefix function to an expression */
Expr * Expr::prefun_to_expr ( float ( *func_ptr ) ( void * ), Expr ** expr_list, int num_args )
{
PrefunExpr * prefun_expr;
if (num_args == 1)
prefun_expr = new PrefunExprOne();
else
prefun_expr = new PrefunExpr();
prefun_expr->num_args = num_args;
prefun_expr->func_ptr = ( float ( * ) ( void* ) ) func_ptr;
prefun_expr->expr_list = expr_list;
return prefun_expr;
}
/* Creates a new tree expression */
TreeExpr::TreeExpr ( InfixOp * _infix_op, Expr * _gen_expr, TreeExpr * _left, TreeExpr * _right ) :
Expr( TREE ),
infix_op ( _infix_op ), gen_expr ( _gen_expr ),
left ( _left ), right ( _right ) {}
class TreeExprAdd : public TreeExpr
{
public:
TreeExprAdd( InfixOp * _infix_op, Expr * _gen_expr, TreeExpr * _left, TreeExpr * _right ) :
TreeExpr( _infix_op, _gen_expr, _left, _right) {}
float eval( int mesh_i, int mesh_j)
{
return left->eval(mesh_i, mesh_j) + right->eval(mesh_i, mesh_j);
}
};
class TreeExprMinus : public TreeExpr
{
public:
TreeExprMinus( InfixOp * _infix_op, Expr * _gen_expr, TreeExpr * _left, TreeExpr * _right ) :
TreeExpr( _infix_op, _gen_expr, _left, _right) {}
float eval( int mesh_i, int mesh_j)
{
return left->eval(mesh_i, mesh_j) - right->eval(mesh_i, mesh_j);
}
};
class TreeExprMult : public TreeExpr
{
public:
TreeExprMult( InfixOp * _infix_op, Expr * _gen_expr, TreeExpr * _left, TreeExpr * _right ) :
TreeExpr( _infix_op, _gen_expr, _left, _right) {}
float eval( int mesh_i, int mesh_j)
{
return left->eval(mesh_i, mesh_j) * right->eval(mesh_i, mesh_j);
}
};
TreeExpr * TreeExpr::create( InfixOp * _infix_op, Expr * _gen_expr, TreeExpr * _left, TreeExpr * _right )
{
if ( NULL != _infix_op )
{
if ( _infix_op->type == INFIX_ADD )
return new TreeExprAdd( _infix_op, _gen_expr, _left, _right);
if ( _infix_op->type == INFIX_MINUS )
return new TreeExprMinus( _infix_op, _gen_expr, _left, _right);
if ( _infix_op->type == INFIX_MULT )
return new TreeExprMult( _infix_op, _gen_expr, _left, _right);
}
return new TreeExpr( _infix_op, _gen_expr, _left, _right );
}
/* Frees a function in prefix notation */
PrefunExpr::~PrefunExpr()
{
int i;
/* Free every element in expression list */
for ( i = 0 ; i < num_args; i++ )
{
delete expr_list[i];
}
free ( expr_list );
}
/* Frees a tree expression */
TreeExpr::~TreeExpr()
{
/* free left tree */
if ( left != NULL )
{
delete left;
}
/* free general expression object */
if ( gen_expr != NULL )
{
delete gen_expr;
}
/* Note that infix operators are always
stored in memory unless the program
exits, so we don't remove them here */
/* free right tree */
if ( right != NULL )
{
delete right;
}
}
/* Initializes an infix operator */
InfixOp::InfixOp ( int _type, int _precedence )
{
this->type = _type;
this->precedence = _precedence;
}
PrefunExpr::PrefunExpr() : Expr(FUNCTION)
{
}
Expr *PrefunExpr::optimize()
{
bool constant_args = true;
for (int i=0 ; i < num_args ; i++)
{
Expr *orig = expr_list[i];
expr_list[i] = orig->optimize();
if (orig != expr_list[i])
delete orig;
constant_args &= expr_list[i]->isConstant();
}
// TODO most functions can be pre-evaluated if inputs are constant, but not all
return this;
}
std::ostream& PrefunExpr::to_string(std::ostream& out)
{
char comma = ' ';
out << "<function>(";
for (int i=0 ; i < num_args ; i++)
{
out << comma;
out << expr_list[i];
comma = ',';
}
out << ")";
return out;
}