| |
| |
|
|
| #include "tconvert.h" |
| |
| |
| #include "tdoubleparam.h" |
| #include "tparamcontainer.h" |
| #include "tparamset.h" |
| #include "texpression.h" |
| #include "tparser.h" |
| #include "tfx.h" |
| |
| #include "tw/stringtable.h" |
| #include "tunit.h" |
| |
| |
| #include "toonz/txsheet.h" |
| #include "toonz/txshcell.h" |
| #include "toonz/tstageobjectid.h" |
| #include "toonz/tstageobject.h" |
| #include "toonz/fxdag.h" |
| |
| |
| #include "boost/noncopyable.hpp" |
| |
| |
| #include <QString> |
| |
| #include "toonz/txsheetexpr.h" |
| |
| using namespace TSyntax; |
| |
| |
| |
| |
| |
| namespace |
| { |
| |
| |
| |
| class ParamDependencyFinder : public TSyntax::CalculatorNodeVisitor |
| { |
| TDoubleParam *m_possiblyDependentParam; |
| bool m_found; |
| |
| public: |
| ParamDependencyFinder(TDoubleParam *possiblyDependentParam) |
| : m_possiblyDependentParam(possiblyDependentParam), m_found(false) {} |
| |
| void check(TDoubleParam *param) |
| { |
| if (param == m_possiblyDependentParam) |
| m_found = true; |
| } |
| |
| bool found() const { return m_found; } |
| }; |
| |
| |
| |
| |
| |
| |
| |
| class ParamCalculatorNode : public CalculatorNode, public TParamObserver, public boost::noncopyable |
| { |
| TDoubleParamP m_param; |
| std::auto_ptr<CalculatorNode> m_frame; |
| |
| public: |
| ParamCalculatorNode(Calculator *calculator, |
| const TDoubleParamP ¶m, |
| std::auto_ptr<CalculatorNode> frame) |
| : CalculatorNode(calculator), m_param(param), m_frame(frame) |
| { |
| param->addObserver(this); |
| } |
| |
| ~ParamCalculatorNode() |
| { |
| m_param->removeObserver(this); |
| } |
| |
| double compute(double vars[3]) const |
| { |
| double value = m_param->getValue(m_frame->compute(vars) - 1); |
| TMeasure *measure = m_param->getMeasure(); |
| if (measure) { |
| const TUnit *unit = measure->getCurrentUnit(); |
| if (unit) |
| value = unit->convertTo(value); |
| } |
| return value; |
| } |
| |
| void accept(TSyntax::CalculatorNodeVisitor &visitor) |
| { |
| ParamDependencyFinder *pdf = dynamic_cast<ParamDependencyFinder *>(&visitor); |
| pdf->check(m_param.getPointer()); |
| m_param->accept(visitor); |
| } |
| |
| void onChange(const TParamChange ¶mChange) |
| { |
| |
| |
| |
| |
| |
| |
| if (TDoubleParam *ownerParam = getCalculator()->getOwnerParameter()) { |
| const std::set<TParamObserver *> &observers = ownerParam->observers(); |
| |
| TParamChange propagatedChange( |
| ownerParam, 0, 0, false, paramChange.m_dragging, paramChange.m_undoing); |
| |
| std::set<TParamObserver *>::const_iterator ot, oEnd = observers.end(); |
| for (ot = observers.begin(); ot != oEnd; ++ot) |
| (*ot)->onChange(propagatedChange); |
| } |
| } |
| }; |
| |
| |
| |
| class XsheetDrawingCalculatorNode : public CalculatorNode, public boost::noncopyable |
| { |
| TXsheet *m_xsh; |
| int m_columnIndex; |
| |
| std::auto_ptr<CalculatorNode> m_frame; |
| |
| public: |
| XsheetDrawingCalculatorNode( |
| Calculator *calc, |
| TXsheet *xsh, |
| int columnIndex, |
| std::auto_ptr<CalculatorNode> frame) |
| : CalculatorNode(calc), m_xsh(xsh), m_columnIndex(columnIndex), m_frame(frame) {} |
| |
| double compute(double vars[3]) const |
| { |
| double f = m_frame->compute(vars); |
| int i = tfloor(f); |
| f = f - (double)i; |
| TXshCell cell; |
| cell = m_xsh->getCell(i, m_columnIndex); |
| int d0 = cell.isEmpty() ? 0 : cell.m_frameId.getNumber(); |
| cell = m_xsh->getCell(i + 1, m_columnIndex); |
| int d1 = cell.isEmpty() ? 0 : cell.m_frameId.getNumber(); |
| double d = (1 - f) * d0 + f * d1; |
| return d; |
| } |
| |
| void accept(TSyntax::CalculatorNodeVisitor &) {} |
| }; |
| |
| |
| |
| |
| |
| |
| |
| class XsheetReferencePattern : public Pattern |
| { |
| TXsheet *m_xsh; |
| |
| public: |
| XsheetReferencePattern(TXsheet *xsh) : m_xsh(xsh) |
| { |
| setDescription( |
| string("object.action\nTransformation reference\n") + |
| "object can be: tab, table, cam<n>, camera<n>, col<n>, peg<n>, pegbar<n>\n" + |
| "action can be: ns,ew,rot,ang,angle,z,zdepth,sx,sy,sc,scale,scalex,scaley,path,pos,shx,shy"); |
| } |
| |
| TStageObjectId matchObjectName(const Token &token) const |
| { |
| string s = toLower(token.getText()); |
| int len = (int)s.length(), i, j; |
| for (i = 0; i < len && isascii(s[i]) && isalpha(s[i]); i++) { |
| } |
| if (i == 0) |
| return TStageObjectId::NoneId; |
| string a = s.substr(0, i); |
| int index = 0; |
| for (j = i; j < len && isascii(s[j]) && isdigit(s[j]); j++) |
| index = index * 10 + (s[j] - '0'); |
| if (j < len) |
| return TStageObjectId::NoneId; |
| if (i == j) |
| index = -1; |
| if ((a == "table" || a == "tab") && index < 0) |
| return TStageObjectId::TableId; |
| else if (a == "col" && index >= 1) |
| return TStageObjectId::ColumnId(index - 1); |
| else if ((a == "cam" || a == "camera") && index >= 1) |
| return TStageObjectId::CameraId(index - 1); |
| else if ((a == "peg" || a == "pegbar") && index >= 1) |
| return TStageObjectId::PegbarId(index - 1); |
| else |
| return TStageObjectId::NoneId; |
| } |
| |
| TStageObject::Channel matchChannelName(const Token &token) const |
| { |
| string s = toLower(token.getText()); |
| if (s == "ns") |
| return TStageObject::T_Y; |
| else if (s == "ew") |
| return TStageObject::T_X; |
| else if (s == "rot" || s == "ang" || s == "angle") |
| return TStageObject::T_Angle; |
| else if (s == "z" || s == "zdepth") |
| return TStageObject::T_Z; |
| else if (s == "sx" || s == "scalex" || s == "xscale" || s == "xs" || s == "sh" || s == "scaleh" || s == "hscale" || s == "hs") |
| return TStageObject::T_ScaleX; |
| else if (s == "sy" || s == "scaley" || s == "yscale" || s == "ys" || s == "sv" || s == "scalev" || s == "vscale" || s == "vs") |
| return TStageObject::T_ScaleY; |
| else if (s == "sc" || s == "scale") |
| return TStageObject::T_Scale; |
| else if (s == "path" || s == "pos") |
| return TStageObject::T_Path; |
| else if (s == "shearx" || s == "shx" || s == "shearh" || s == "shh") |
| return TStageObject::T_ShearX; |
| else if (s == "sheary" || s == "shy" || s == "shearv" || s == "shv") |
| return TStageObject::T_ShearY; |
| else |
| return TStageObject::T_ChannelCount; |
| } |
| |
| bool expressionExpected(const std::vector<Token> &previousTokens) const |
| { |
| return previousTokens.size() == 4; |
| } |
| bool matchToken(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| int i = (int)previousTokens.size(); |
| if (i == 0) |
| return matchObjectName(token) != TStageObjectId::NoneId; |
| else if (i == 1 && token.getText() == "." || i == 3 && token.getText() == "(" || i == 5 && token.getText() == ")") |
| return true; |
| else if (i == 2) { |
| if (matchChannelName(token) < TStageObject::T_ChannelCount) |
| return true; |
| else |
| return token.getText() == "cell" && |
| matchObjectName(previousTokens[0]).isColumn(); |
| } else |
| return false; |
| } |
| bool isFinished(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return previousTokens.size() >= 6; |
| } |
| bool isComplete(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return previousTokens.size() >= 6 || previousTokens.size() == 3; |
| } |
| TSyntax::TokenType getTokenType(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return TSyntax::Operator; |
| } |
| |
| void getAcceptableKeywords(std::vector<std::string> &keywords) const |
| { |
| const string ks[] = {"table", "tab", "col", "cam", "camera", "peg", "pegbar"}; |
| for (int i = 0; i < tArrayCount(ks); i++) |
| keywords.push_back(ks[i]); |
| } |
| |
| void createNode( |
| Calculator *calc, |
| std::vector<CalculatorNode *> &stack, |
| const std::vector<Token> &tokens) const |
| { |
| assert(tokens.size() >= 3); |
| |
| std::auto_ptr<CalculatorNode> frameNode((tokens.size() == 6) ? popNode(stack) : new VariableNode(calc, CalculatorNode::FRAME)); |
| |
| TStageObjectId objectId = matchObjectName(tokens[0]); |
| |
| string field = toLower(tokens[2].getText()); |
| if (field == "cell" || field == "cel" || field == "cels") { |
| int columnIndex = objectId.getIndex(); |
| stack.push_back(new XsheetDrawingCalculatorNode(calc, m_xsh, columnIndex, frameNode)); |
| } else { |
| TStageObject *object = m_xsh->getStageObject(objectId); |
| TStageObject::Channel channelName = matchChannelName(tokens[2]); |
| TDoubleParam *channel = object->getParam(channelName); |
| if (channel) |
| stack.push_back(new ParamCalculatorNode(calc, channel, frameNode)); |
| } |
| } |
| }; |
| |
| |
| |
| class FxReferencePattern : public Pattern |
| { |
| TXsheet *m_xsh; |
| |
| public: |
| FxReferencePattern(TXsheet *xsh) : m_xsh(xsh) {} |
| |
| TFx *getFx(const Token &token) const |
| { |
| return m_xsh->getFxDag()->getFxById(toWideString(toLower(token.getText()))); |
| } |
| TParam *getParam(const TFx *fx, const Token &token) const |
| { |
| int i; |
| for (i = 0; i < fx->getParams()->getParamCount(); i++) { |
| TParam *param = fx->getParams()->getParam(i); |
| string paramName = toString(TStringTable::translate(fx->getFxType() + "." + param->getName())); |
| int i = paramName.find(" "); |
| while (i != string::npos) { |
| paramName.erase(i, 1); |
| i = paramName.find(" "); |
| } |
| string paramNameToCheck = token.getText(); |
| if (paramName == paramNameToCheck || toLower(paramName) == toLower(paramNameToCheck)) |
| return param; |
| } |
| return 0; |
| } |
| TParam *getLeafParam(const TFx *fx, const TParamSet *paramSet, const Token &token) const |
| { |
| int i; |
| for (i = 0; i < paramSet->getParamCount(); i++) { |
| TParam *param = paramSet->getParam(i).getPointer(); |
| string paramName = param->getName(); |
| int i = paramName.find(" "); |
| while (i != string::npos) { |
| paramName.erase(i, 1); |
| i = paramName.find(" "); |
| } |
| string paramNameToCheck = token.getText(); |
| if (paramName == paramNameToCheck || toLower(paramName) == toLower(paramNameToCheck)) |
| return param; |
| } |
| return 0; |
| } |
| string getFirstKeyword() const { return "fx"; } |
| bool expressionExpected(const std::vector<Token> &previousTokens) const |
| { |
| return !previousTokens.empty() && previousTokens.back().getText() == "("; |
| } |
| bool matchToken(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| int i = (int)previousTokens.size(); |
| string s = toLower(token.getText()); |
| if (i == 0 && s == "fx") |
| return true; |
| else if (i == 1) |
| return s == "."; |
| else if (i & 1) { |
| if (previousTokens[i - 2].getText() == "(") |
| return s == ")"; |
| else |
| return s == "." || s == "("; |
| } else if (i == 2) { |
| |
| return getFx(token) != 0; |
| } else if (i == 4) { |
| TFx *fx = getFx(previousTokens[2]); |
| if (!fx) |
| return false; |
| TParam *param = getParam(fx, token); |
| return !!param; |
| } else if (i == 6) { |
| TFx *fx = getFx(previousTokens[2]); |
| if (!fx) |
| return false; |
| TParam *param = getParam(fx, previousTokens[4]); |
| TParamSet *paramSet = dynamic_cast<TParamSet *>(param); |
| if (!paramSet) |
| return false; |
| TParam *leafParam = getLeafParam(fx, paramSet, token); |
| return !!param; |
| } else |
| return false; |
| } |
| bool isFinished(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return !previousTokens.empty() && previousTokens.back().getText() == ")"; |
| } |
| bool isComplete(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| int n = (int)previousTokens.size(); |
| return n >= 2 && (n & 1) == 1 && previousTokens[n - 2].getText() != "("; |
| } |
| TSyntax::TokenType getTokenType(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return TSyntax::Operator; |
| } |
| |
| void createNode( |
| Calculator *calc, |
| std::vector<CalculatorNode *> &stack, |
| const std::vector<Token> &tokens) const |
| { |
| int tokenSize = tokens.size(); |
| |
| std::auto_ptr<CalculatorNode> frameNode((tokenSize > 0 && tokens.back().getText() == ")") ? popNode(stack) : new VariableNode(calc, CalculatorNode::FRAME)); |
| |
| TFx *fx = getFx(tokens[2]); |
| if (!fx || tokenSize < 4) |
| return; |
| |
| TParamP param = getParam(fx, tokens[4]); |
| if (!param.getPointer()) |
| return; |
| |
| TDoubleParamP channel; |
| TParamSet *paramSet = dynamic_cast<TParamSet *>(param.getPointer()); |
| |
| if (paramSet && tokenSize > 6) |
| channel = dynamic_cast<TDoubleParam *>(getLeafParam(fx, paramSet, tokens[6])); |
| else |
| channel = param; |
| |
| if (channel.getPointer()) |
| stack.push_back(new ParamCalculatorNode(calc, channel, frameNode)); |
| } |
| }; |
| |
| |
| |
| class PlasticVertexPattern : public Pattern |
| { |
| TXsheet *m_xsh; |
| |
| |
| |
| |
| |
| |
| |
| |
| enum Positions { |
| OBJECT, |
| L1, |
| COLUMN_NUMBER, |
| COMMA, |
| QUOTE1, |
| VERTEX_NAME, |
| QUOTE2, |
| R1, |
| SELECTOR, |
| COMPONENT, |
| L2, |
| EXPR, |
| R2, |
| POSITIONS_COUNT |
| }; |
| |
| public: |
| PlasticVertexPattern(TXsheet *xsh) |
| : m_xsh(xsh) |
| { |
| setDescription( |
| "vertex(columnNumber, \"vertexName\").action\nVertex data\n" |
| "columnNumber must be the number of the column containing the desired skeleton\n" |
| "vertexName must be the name of a Plastic Skeleton vertex\n" |
| "action must be one of the parameter names available for a Plastic Skeleton vertex"); |
| } |
| |
| virtual string getFirstKeyword() const { return "vertex"; } |
| |
| bool expressionExpected(const std::vector<Token> &previousTokens) const |
| { |
| return (previousTokens.size() == EXPR); |
| } |
| |
| bool matchToken(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| struct { |
| const PlasticVertexPattern *m_this; |
| const SkD *skdp(const Token &columnToken) |
| { |
| int colIdx = columnToken.getIntValue() - 1; |
| |
| if (!m_this->m_xsh->isColumnEmpty(colIdx)) { |
| TStageObject *obj = m_this->m_xsh->getStageObject(TStageObjectId::ColumnId(colIdx)); |
| assert(obj); |
| |
| if (const SkDP &skdp = obj->getPlasticSkeletonDeformation()) |
| return skdp.getPointer(); |
| } |
| |
| return 0; |
| } |
| } locals = {this}; |
| |
| const std::string &text = token.getText(); |
| int pos = previousTokens.size(); |
| |
| if (!m_fixedTokens[pos].empty()) |
| return (text == m_fixedTokens[pos]); |
| |
| switch (pos) { |
| case COLUMN_NUMBER: |
| return (token.getType() == Token::Number && locals.skdp(token)); |
| |
| CASE VERTEX_NAME: |
| { |
| if (const SkD *skdp = locals.skdp(previousTokens[COLUMN_NUMBER])) { |
| const QString &vertexName = QString::fromStdString(text); |
| return (skdp->vertexDeformation(vertexName) != 0); |
| } |
| } |
| |
| CASE COMPONENT : return std::count( |
| m_components, m_components + sizeof(m_components) / sizeof(Component), |
| text) > 0; |
| } |
| |
| return false; |
| } |
| |
| bool isFinished(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return (previousTokens.size() >= POSITIONS_COUNT); |
| } |
| |
| bool isComplete(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return (previousTokens.size() >= POSITIONS_COUNT || previousTokens.size() == L2); |
| } |
| |
| TSyntax::TokenType getTokenType(const std::vector<Token> &previousTokens, const Token &token) const |
| { |
| return TSyntax::Operator; |
| } |
| |
| void createNode(Calculator *calc, std::vector<CalculatorNode *> &stack, |
| const std::vector<Token> &tokens) const |
| { |
| assert(tokens.size() > COMPONENT); |
| |
| std::auto_ptr<CalculatorNode> frameNode((tokens.size() == POSITIONS_COUNT) ? popNode(stack) : new VariableNode(calc, CalculatorNode::FRAME)); |
| |
| int colIdx = tokens[COLUMN_NUMBER].getIntValue() - 1; |
| if (!m_xsh->isColumnEmpty(colIdx)) { |
| TStageObject *obj = m_xsh->getStageObject(TStageObjectId::ColumnId(colIdx)); |
| assert(obj); |
| |
| if (const SkDP &skdp = obj->getPlasticSkeletonDeformation()) { |
| const QString &vertexName = QString::fromStdString(tokens[VERTEX_NAME].getText()); |
| if (SkVD *skvd = skdp->vertexDeformation(vertexName)) { |
| const Component *componentsEnd = m_components + sizeof(m_components) / sizeof(Component), |
| *component = std::find(m_components, componentsEnd, tokens[COMPONENT].getText()); |
| |
| if (component != componentsEnd) { |
| const TDoubleParamP ¶m = skvd->m_params[component->m_paramId].getPointer(); |
| stack.push_back(new ParamCalculatorNode(calc, param, frameNode)); |
| } |
| } |
| } |
| } |
| } |
| |
| private: |
| struct Component { |
| std::string m_name; |
| SkVD::Params m_paramId; |
| |
| bool operator==(const std::string &name) const |
| { |
| return (m_name == name); |
| } |
| }; |
| |
| private: |
| static const std::string m_fixedTokens[POSITIONS_COUNT]; |
| static const Component m_components[5]; |
| }; |
| |
| const std::string PlasticVertexPattern::m_fixedTokens[POSITIONS_COUNT] = { |
| "vertex", "(", "", ",", "\"", "", "\"", ")", ".", "", "(", "", ")"}; |
| |
| const PlasticVertexPattern::Component PlasticVertexPattern::m_components[] = { |
| {"ang", SkVD::ANGLE}, {"angle", SkVD::ANGLE}, {"dist", SkVD::DISTANCE}, {"distance", SkVD::DISTANCE}, {"so", SkVD::SO}}; |
| |
| } |
| |
| |
| |
| |
| |
| TSyntax::Grammar *createXsheetGrammar(TXsheet *xsh) |
| { |
| Grammar *grammar = new Grammar(); |
| grammar->addPattern(new XsheetReferencePattern(xsh)); |
| grammar->addPattern(new FxReferencePattern(xsh)); |
| grammar->addPattern(new PlasticVertexPattern(xsh)); |
| return grammar; |
| } |
| |
| bool dependsOn(TExpression &expr, TDoubleParam *possiblyDependentParam) |
| { |
| ParamDependencyFinder pdf(possiblyDependentParam); |
| expr.accept(pdf); |
| return pdf.found(); |
| } |
| |
| bool dependsOn(TDoubleParam *param, TDoubleParam *possiblyDependentParam) |
| { |
| ParamDependencyFinder pdf(possiblyDependentParam); |
| param->accept(pdf); |
| return pdf.found(); |
| } |
| |