function.cpp

Go to the documentation of this file.
00001 /*
00002  * function.cpp
00003  *
00004  * Copyright (C) 2007,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of vgfx functions (see vgfx.h function API)
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "vgfx.h"               // always include our own header first!
00036 
00037 #include <fstream>
00038 
00039 #include "common/wave_ex.h"
00040 #include "perf/perf.h"
00041 #include "util/file.h"
00042 #include "util/parsing.h"
00043 #include "util/token_stream.h"
00044 
00045 
00046 namespace vgfx {
00047 
00048 static const eParseBehavior s_line_behavior = (eParseBehavior) (
00049         eParse_StripComments |
00050         eParse_StripBogus |
00051         eParse_RespectQuotes
00052         );
00053 
00054 
00055 ////////////////////////////////////////////////////////////////////////////////
00056 //
00057 //      static helper methods
00058 //
00059 ////////////////////////////////////////////////////////////////////////////////
00060 
00061 static bool
00062 requiresQuotes
00063 (
00064 IN const char * p
00065 )
00066 throw()
00067 {
00068         ASSERT(p, "null string passed to requiresQuotes()");
00069 
00070         if (!*p)
00071                 return true;    // empty string
00072 
00073         while (*p) {
00074                 if (isspace(*p))
00075                         return true;
00076                 ++p;
00077         }
00078 
00079         return false;
00080 }
00081 
00082 
00083 
00084 static void
00085 getFunctionValue
00086 (
00087 IN ObjectMap * map,
00088 const char * fn_name,
00089 OUT std::string& value
00090 )
00091 {
00092         ASSERT(map, "null object map");
00093         ASSERT(fn_name, "null function name");
00094 
00095         // TODO: support real functions!
00096         if (!strcmp("_newID()", fn_name)) {
00097                 // okay, get a free ID
00098                 map->newObjectID(NULL, value);
00099         } else {
00100                 WAVE_EX(wex);
00101                 wex << "Unknown function: '" << fn_name << "'";
00102         }
00103 
00104 }
00105 
00106 
00107 
00108 static bool
00109 isOperation
00110 (
00111 IN const char * s
00112 )
00113 throw()
00114 {
00115         ASSERT(s, "null string passed in to isOperation()");
00116 
00117         if (!strcmp("-", s) ||
00118             !strcmp("+", s) ||
00119             !strcmp("*", s) ||
00120             !strcmp("/", s))
00121                 return true;
00122         return false;
00123 }
00124 
00125 
00126 
00127 static float
00128 operate
00129 (
00130 IN float left,
00131 IN char op,
00132 IN float right
00133 )
00134 throw()
00135 {
00136         switch (op) {
00137         case '-': return left - right;
00138         case '+': return left + right;
00139         case '*': return left * right;
00140         case '/': return left / right;
00141         }
00142 
00143         WAVE_EX(wex);
00144         wex << "Unrecognized operation! '" << op << "'";
00145         return -1;
00146 }
00147 
00148 
00149 
00150 static void
00151 computeValue
00152 (
00153 IN ObjectMap * map,
00154 IN const char * raw,
00155 IN const dictionary_t& variables,
00156 OUT std::string& computed
00157 );
00158 
00159 
00160 
00161 static void
00162 evaluate
00163 (
00164 IN ObjectMap * map,
00165 IN const dictionary_t& variables,
00166 IN const char * expr,
00167 OUT std::string& result
00168 )
00169 {
00170         ASSERT(map, "null object map in evaluate()");
00171         ASSERT(expr, "null expression");
00172 
00173         result.clear();
00174 
00175         // keep parsing expression
00176         DPRINTF("WARNING: expression parsing is still primitive");
00177 
00178         std::string token;
00179         float value = 0.0;
00180         char operation = 0;
00181         bool have_left = false;
00182         const char * cursor = expr;
00183         while (true) {
00184                 cursor = getNextTokenFromString(cursor, token, eParse_None);
00185                 if (!token[0])
00186                         break;  // no more tokens
00187 
00188                 float right = 0.0;
00189                 bool have_right = false;
00190                 if (!isOperation(token.c_str())) {
00191                         std::string val;
00192                         computeValue(map, token.c_str(), variables, val);
00193                         right = atof(val.c_str());
00194                         // DPRINTF("'%s' --> %5.2f", token, right);
00195                         have_right = true;
00196                 } else {
00197                         if (operation) {
00198                                 WAVE_EX(wex);
00199                                 wex << "encountered two operators in a row?  '";
00200                                 wex << operation << "' and '" << token << "'";
00201                         }
00202                         operation = token[0];
00203                 }
00204 
00205                 // should we evaluate something?
00206                 if (have_right) {
00207                         if (!have_left) {
00208                                 // this is the first token
00209                                 have_left = true;
00210                                 have_right = false;
00211                                 value = right;
00212                         } else {
00213                                 // left + right -- operation?
00214                                 if (!operation) {
00215                                         WAVE_EX(wex);
00216                                         wex << "Have left and right values, ";
00217                                         wex << "but no operator between them!";
00218                                 }
00219 
00220                                 value = operate(value, operation, right);
00221                                 have_left = true;
00222                                 have_right = false;
00223                                 operation = 0;
00224                         }
00225                 }
00226         }
00227 
00228         const int bufsize = 32;
00229         char buffer[bufsize];
00230         sprintf(buffer, "%8.6f", value);
00231         result = buffer;
00232 }
00233 
00234 
00235 
00236 static void
00237 computeValue
00238 (
00239 IN ObjectMap * map,
00240 IN const char * raw,
00241 IN const dictionary_t& variables,
00242 OUT std::string& computed
00243 )
00244 {
00245         ASSERT(raw, "null raw value");
00246         computed.clear();
00247 
00248         if ('$' == raw[0]) {
00249                 // variable
00250 //              DPRINTF("looking for variable '%s'...", raw + 1);
00251                 computed = getRequiredValue(variables, raw + 1);
00252         } else if ('&' == raw[0]) {
00253                 // function call
00254 //              DPRINTF("invoking function '%s'...", raw + 1);
00255                 getFunctionValue(map, raw + 1, computed);
00256         } else if ('=' == raw[0]) {
00257 //              DPRINTF("attempting to evaluate expression '%s'...", raw + 1);
00258                 evaluate(map, variables, raw + 1, computed);
00259         } else {
00260                 computed = raw;
00261         }
00262 }
00263 
00264 
00265 
00266 static float
00267 getFloat
00268 (
00269 IN ObjectMap * map,
00270 IN const dictionary_t& variables,
00271 IN const dictionary_t& data,
00272 IN const char * key,
00273 IN const char * default_value
00274 )
00275 {
00276         ASSERT(key, "null key name in getFloat()");
00277         ASSERT(default_value, "null default value in getFloat()");
00278 
00279         const char * value = NULL;
00280         if (default_value)
00281                 value = getOptionalValue(data, key, default_value);
00282         else
00283                 value = getRequiredValue(data, key);
00284         ASSERT(value, "should have value now");
00285 
00286         std::string computed;
00287         computeValue(map, value, variables, computed);
00288 
00289         return atof(computed.c_str());
00290 }
00291 
00292 
00293 
00294 static void
00295 processTile
00296 (
00297 IN ObjectMap * map,
00298 IN const char * cursor,
00299 IN const dictionary_t& variables,
00300 IO std::ostream& stream
00301 )
00302 {
00303         ASSERT(map, "null object map in processTile()");
00304         ASSERT(cursor, "null tile declaration line");
00305         ASSERT(stream.good(), "bad stream in processTile()");
00306 
00307         dictionary_t data;
00308         getDictionaryFromString(cursor, "parsing tile declaration line", data);
00309 
00310         // get x/y tiling parameters
00311         float x_start = getFloat(map, variables, data, "xStart", "0.0");
00312         float x_inc = getFloat(map, variables, data, "xInc", "0.0");
00313         float x_end = getFloat(map, variables, data, "xEnd", "0.0");
00314         if (x_end != x_start && !x_inc) {
00315                 WAVE_EX(wex);
00316                 wex << "Cannot tile in x-direction: missing increment 'xInc'";
00317         }
00318         if (x_inc < 0) {
00319                 WAVE_EX(wex);
00320                 wex << "Bad x-increment: " << x_inc;
00321         }
00322 
00323         float y_start = getFloat(map, variables, data, "yStart", "0.0");
00324         float y_inc = getFloat(map, variables, data, "yInc", "0.0");
00325         float y_end = getFloat(map, variables, data, "yEnd", "0.0");
00326         if (y_end != y_start && !y_inc) {
00327                 WAVE_EX(wex);
00328                 wex << "Cannot tile in y-direction: missing increment 'yInc'";
00329         }
00330         if (y_inc < 0) {
00331                 WAVE_EX(wex);
00332                 wex << "Bad y-increment: " << y_inc;
00333         }
00334 
00335         // okay, tile!
00336         const char * raw_id = getRequiredValue(data, "id");
00337         std::string id;
00338         computeValue(map, raw_id, variables, id);
00339 //      DPRINTF("Looping x from %5.2f to %5.2f with increment %5.2f",
00340 //          x_start, x_end, x_inc);
00341 //      DPRINTF("Looping y from %5.2f to %5.2f with increment %5.2f",
00342 //          y_start, y_end, y_inc);
00343         float x = x_start;
00344         while (x <= x_end) {
00345 
00346                 float y = y_start;
00347                 while (y <= y_end) {
00348                         std::string tag_id;
00349                         map->newObjectID("tag", tag_id);
00350                         stream << "object tag " << tag_id << " id " << id;
00351                         stream << " x " << x << " y " << y << "\n";
00352 
00353                         if (!y_inc)
00354                                 break;
00355                         y += y_inc;
00356                 }
00357 
00358                 if (!x_inc)
00359                         break;
00360                 x += x_inc;
00361         }
00362 }
00363 
00364 
00365 
00366 static void
00367 processVariable
00368 (
00369 IN ObjectMap * map,
00370 IN const char * cursor,
00371 IN const dictionary_t& in_params,
00372 IN const objtree::property_set_t& pub,
00373 IO dictionary_t& variables
00374 )
00375 {
00376         ASSERT(map, "null object map in processVariable()");
00377         ASSERT(cursor, "null variable declaration line");
00378 
00379         dictionary_t data;
00380         getDictionaryFromString(cursor, "parsing variable declaration line",
00381             data);
00382 
00383         // must declare a name
00384         const char * name = getRequiredValue(data, "name");
00385         if (!objtree::isValidPropertyName(name)) {
00386                 WAVE_EX(wex);
00387                 wex << "Invalid variable name: '" << name << "'";
00388         }
00389 
00390         const char * value = getValue(in_params, name);
00391         if (!value) {
00392                 // not passed in -- public?
00393                 value = pub.getValue(name);
00394                 if (!value) {
00395                         // not passed in or public -- value provided in script?
00396                         value = getValue(data, "value");
00397                         if (!value) {
00398                                 WAVE_EX(wex);
00399                                 wex << "variable '" << name << "' has ";
00400                                 wex << "no default value, and no value was ";
00401                                 wex << "passed in by caller.";
00402                         }
00403                 }
00404         }
00405         ASSERT(value, "Should have value for variable '%s'", name);
00406 
00407         // execute value if necessary
00408         std::string computed;
00409         computeValue(map, value, variables, computed);
00410 
00411         // set in variable map
00412         variables[name] = computed;
00413 //      DPRINTF("Added variable '%s' == '%s'", name, computed.c_str());
00414 }
00415 
00416 
00417 
00418 ////////////////////////////////////////////////////////////////////////////////
00419 //
00420 //      Function -- class that implements vgfx::Primitive interface for fns
00421 //
00422 ////////////////////////////////////////////////////////////////////////////////
00423 
00424 class Function : public Primitive {
00425 public:
00426         ~Function(void) throw() { }
00427 
00428         // public class methods ------------------------------------------------
00429         void initialize(IN std::istream& stream);
00430         const VecString& getLines(void) const throw() { return m_lines; }
00431         const objtree::property_set_t& getPublic(void) const throw()
00432                                 { return m_public; }
00433 
00434         // vgfx::Primitive class interface methods -----------------------------
00435         const char * getType(void) const throw() { return "function"; }
00436         void persist(OUT std::ostream& stream) const;
00437         void listContainers(IN const VecString& path,
00438                                 OUT VecString& ids) const;
00439         bool doesContainerExist(IN const VecString& path) const;
00440         bool canCreateContainer(IN const VecString& path) const;
00441         void removeContainer(IN const VecString& path);
00442         void getContainerDictionary(IN const VecString& path,
00443                                 OUT dictionary_t& data) const;
00444         void setContainerDictionary(IN const VecString& path,
00445                                 IN const dictionary_t& data);
00446         void recalcBoundingRect(IN const char * tag_path,
00447                                 IN Drawer *, IN const xform_2d_t&) { }
00448         bool getBoundingRect(OUT rect_t& r) const throw() { return false; }
00449         void draw(IN Drawer * drawer,
00450                                 IN const rect_t& r_cm,
00451                                 IN const xform_2d_t& T) { }
00452 
00453 private:
00454         // private member data -------------------------------------------------
00455         VecString               m_lines;        // actual function data
00456         objtree::property_set_t m_public;       // public values
00457 };
00458 
00459 
00460 
00461 void
00462 Function::persist
00463 (
00464 OUT std::ostream& stream
00465 )
00466 const
00467 {
00468         stream << "function {\n";
00469         stream << "\tid\t" << this->getID() << "\n";
00470         for (VecString::const_iterator i = m_lines.begin(); i != m_lines.end();
00471              ++i) {
00472                 stream << *i << "\n";
00473         }
00474         stream << "}\n";
00475 }
00476 
00477 
00478 
00479 void
00480 Function::listContainers
00481 (
00482 IN const VecString& path,
00483 OUT VecString& ids
00484 )
00485 const
00486 {
00487         ids.clear();
00488         if (1 != path.size() || path[0] != "public") {
00489                 WAVE_EX(wex);
00490                 wex << "Functions only support path='public' in listContainers()";
00491         }
00492 
00493         for (objtree::property_set_t::const_iterator i = m_public.begin();
00494              i != m_public.end(); ++i) {
00495                 ids.push_back(i->first);
00496         }
00497 }
00498 
00499 
00500 
00501 bool
00502 Function::doesContainerExist
00503 (
00504 IN const VecString& path
00505 )
00506 const
00507 {
00508         if (2 != path.size())
00509                 return false;
00510 
00511         if ("public" != path[0])
00512                 return false;
00513 
00514         return !m_public.getProperty(path[1].c_str());
00515 }
00516 
00517 
00518 
00519 bool
00520 Function::canCreateContainer
00521 (
00522 IN const VecString& path
00523 )
00524 const
00525 {
00526         if (2 != path.size())
00527                 return false;
00528         if ("public" != path[0])
00529                 return false;
00530         return objtree::isValidPropertyName(path[1].c_str());
00531 }
00532 
00533 
00534 
00535 void
00536 Function::removeContainer
00537 (
00538 IN const VecString& path
00539 )
00540 {
00541         if (2 != path.size() || "public" != path[0]) {
00542                 WAVE_EX(wex);
00543                 wex << "invalid path in Function::removeContainer()";
00544         }
00545 
00546         ASSERT(false, "who is removing function public properties?");
00547 }
00548 
00549 
00550 
00551 void
00552 Function::getContainerDictionary
00553 (
00554 IN const VecString& path,
00555 OUT dictionary_t& data
00556 )
00557 const
00558 {
00559         data.clear();
00560         if (2 != path.size() || "public" != path[0]) {
00561                 WAVE_EX(wex);
00562                 wex << "invalid path in Function::getContainerDictionary()";
00563         }
00564 
00565         const char * name = path[1].c_str();
00566         const objtree::property_t * p = m_public.getProperty(name);
00567         if (!p) {
00568                 WAVE_EX(wex);
00569                 wex << "Public property '" << name << "' not found in ";
00570                 wex << "function '" << this->getID() << "'";
00571         }
00572 
00573         for (objtree::property_t::const_iterator i = p->begin(); i != p->end();
00574              ++i) {
00575                 const char * field = i->first.c_str();
00576                 const char * value = i->second.c_str();
00577 
00578                 data[field] = value;
00579         }
00580 }
00581 
00582 
00583 
00584 void
00585 Function::setContainerDictionary
00586 (
00587 IN const VecString& path,
00588 IN const dictionary_t& data
00589 )
00590 {
00591         if (2 != path.size() || "public" != path[0]) {
00592                 WAVE_EX(wex);
00593                 wex << "invalid path in Function::setContainerDictionary()";
00594         }
00595 
00596         ASSERT(false, "Who is changing public parameters of functions?");
00597 
00598 /*
00599         objtree::property_set_t::iterator i = m_public.find(path[1]);
00600         if (m_public.end() == i) {
00601                 WAVE_EX(wex);
00602                 wex << "Public property '" << path[1] << "' not found in ";
00603                 wex << "function '" << this->getID() << "'";
00604         }
00605 
00606         objtree::property_t& p = i->second;
00607         p.type = getOptionalValue(data, "type", p.type.c_str());
00608         p.value = getOptionalValue(data, "value", p.value.c_str());
00609 */
00610 }
00611 
00612 
00613 
00614 void
00615 Function::initialize
00616 (
00617 IN std::istream& stream
00618 )
00619 {
00620         ASSERT(stream.good(), "bad stream passed to Function::create()");
00621 
00622         // keep parsing lines until we hit the closing bracket
00623         std::string line, token;
00624         int depth = 0;          // cheesy way to keep track of parse depth
00625         while (true) {
00626                 line = getNextLineFromStream(stream, s_line_behavior);
00627                 const char * cursor =
00628                     getNextTokenFromString(line.c_str(), token, eParse_None);
00629                 if ("" == token) {
00630                         continue;       // empty line
00631                 }
00632 
00633                 if ("variable" == token) {
00634                         // it's a token!
00635                         dictionary_t data;
00636                         getDictionaryFromString(cursor, "parsing variable", data);
00637 
00638                         if (!strcmp("true", getOptionalValue(data, "public", "false"))) {
00639                                 objtree::property_t p;
00640                                 const char * name = getRequiredValue(data, "name");
00641 
00642                                 for (dictionary_t::iterator i = data.begin();
00643                                      i != data.end(); ++i) {
00644                                         const char * field = i->first.c_str();
00645                                         if (!strcmp("name", field))
00646                                                 continue;       // skip this one
00647                                         const char * value = i->second.c_str();
00648                                         p.setFieldValue(field, value);
00649                                 }
00650 
00651                                 m_public[name] = p;
00652                         }
00653                 }
00654 
00655                 // are we diving into a subojbect?
00656                 if (strstr(line.c_str(), "{"))
00657                         ++depth;        // diving into object
00658                 if (strstr(line.c_str(), "}"))
00659                         --depth;        // popping out of object
00660 
00661                 // end of function?
00662                 if (depth < 0) {
00663                         ASSERT(-1 == depth, "Bad depth: %d", depth);
00664                         break;          // hit the final closing bracket
00665                 }
00666 
00667                 // not an empty line: copy into our buffer
00668 
00669                 // only care about global data after this point
00670                 if (depth) {
00671                         m_lines.push_back(line);        // push, don't process
00672                         continue;       // not in global parsing
00673                 }
00674 
00675                 // is this the ID?
00676                 if ("id" == token) {
00677                         getNextTokenFromString(cursor, token, eParse_None);
00678                         this->setID(token.c_str());     // stash ID
00679                         continue;       // don't store this line
00680                 }
00681 
00682                 // store line
00683                 m_lines.push_back(line);
00684         }
00685 
00686 //      DPRINTF("%d lines read for function", m_lines.size());
00687 //      DPRINTF("Function name: '%s'", m_id.c_str());
00688 }
00689 
00690 
00691 
00692 static void
00693 getScriptOutputId
00694 (
00695 IN const char * param_id,
00696 OUT std::string& output_id
00697 )
00698 {
00699         ASSERT(param_id, "null");
00700 
00701         output_id = param_id;
00702         output_id += "ScriptOutput";
00703 }
00704 
00705 
00706 
00707 ////////////////////////////////////////////////////////////////////////////////
00708 //
00709 //      public API
00710 //
00711 ////////////////////////////////////////////////////////////////////////////////
00712 
00713 smart_ptr<Primitive>
00714 parseFunction
00715 (
00716 IN std::istream& stream,
00717 IN ObjectMap * map
00718 )
00719 {
00720         smart_ptr<Function> local = new Function;
00721         ASSERT(local, "out of memory");
00722 
00723         local->initialize(stream);
00724 
00725         return local;
00726 }
00727 
00728 
00729 
00730 void
00731 invokeFunction
00732 (
00733 IN Primitive * in_function,
00734 IN ObjectMap * map,
00735 IN const dictionary_t& in_params,
00736 OUT std::ostream& out
00737 )
00738 {
00739         perf::Timer timer("vgfx::invokeFunction");
00740         ASSERT(in_function, "null function object");
00741         ASSERT(map, "null object map");
00742 
00743         if (strcmp("function", in_function->getType())) {
00744                 WAVE_EX(wex);
00745                 wex << "Object '" << in_function->getID() << "' is not a ";
00746                 wex << "function, it is a '" << in_function->getType() << "'";
00747         }
00748 
00749         Function * function = dynamic_cast<Function *>(in_function);
00750         ASSERT(function, "this should work!");  
00751 
00752         dictionary_t variables;
00753         std::string token;
00754 
00755         const VecString& lines = function->getLines();
00756         for (VecString::const_iterator i = lines.begin(); i != lines.end(); ++i)
00757         {
00758                 const char * line = i->c_str();
00759                 // DPRINTF("Line: '%s'", line);
00760 
00761                 const char * cursor =
00762                     getNextTokenFromString(line, token, eParse_None);
00763                 if ("" == token) {
00764                         DPRINTF("Weird: blank line in function???");
00765                         continue;       // next line
00766                 }
00767 
00768                 // special processing for this line?
00769                 if ("variable" == token) {
00770                         // variable declaration
00771                         processVariable(map, cursor, in_params,
00772                             function->getPublic(), variables);
00773                         continue;       // no more parsing for line
00774                 } else if ("tile" == token) {
00775                         // tile command
00776                         processTile(map, cursor, variables, out);
00777                 } else {
00778                         // just render with search/replace as needed
00779                         out << token << " ";
00780                         while (true) {
00781                                 cursor = getNextTokenFromString(cursor, token,
00782                                     eParse_None);
00783                                 if ("" == token)
00784                                         break;  // end of line
00785 
00786                                 std::string computed;
00787                                 computeValue(map, token.c_str(), variables,
00788                                     computed);
00789                                 const char * value = computed.c_str();
00790                                 if (requiresQuotes(value)) {
00791                                         out << "\"" << value << "\" ";
00792                                 } else {
00793                                         out << value << " ";
00794                                 }
00795                         }
00796                         out << "\n";
00797                 }
00798         }
00799 }
00800 
00801 
00802 
00803 Primitive *
00804 addFunctionFromPath
00805 (
00806 IN ObjectMap * map,
00807 IN const char * path
00808 )
00809 {
00810         ASSERT(map, "null");
00811         ASSERT(path, "null");
00812 
00813         // get root filename
00814         std::string id;
00815         GetFileRoot(path, id);
00816 
00817         // already have this?  Then don't do anything
00818         // NOTE (and TODO): I'd love to remove and re-load, but there
00819         // is a problem: we don't know who else in the map is also
00820         // using this script.  We'd have to find and clear all references
00821         // before removing the object.  So for now, if someone is tweaking
00822         // scripts, they have to reload the app to get an updated script.
00823         Primitive * p = map->findObject(id.c_str());
00824         if (p) {
00825                 DPRINTF("Already loaded script with id='%s'", id.c_str());
00826                 return p;
00827         }
00828 
00829         // need to load it
00830         std::ifstream infile(path);
00831         if (!infile.good()) {
00832                 WAVE_EX(wex);
00833                 wex << "Failed to open file for reading: " << path;
00834         }
00835 
00836         // add to map
00837         std::string unused, diagnostic;
00838         if (!processRequest(map, infile, false, unused, diagnostic)) {
00839                 WAVE_EX(wex);
00840                 wex << "Failed to load script: " << diagnostic;
00841         }
00842 
00843         // verify
00844         p = map->findObject(id.c_str());
00845         if (!p) {
00846                 WAVE_EX(wex);
00847                 wex << "Malformed script: function id should match filename: ";
00848                 wex << "'" << id << "'";
00849         }
00850 
00851         // return
00852         return p;
00853 }
00854 
00855 
00856 
00857 void
00858 setScript
00859 (
00860 IN ObjectMap * map,
00861 IN const char * parent_id,
00862 IN const char * script_id,
00863 IN const objtree::property_set_t& pset_def,     // defaults (may be empty)
00864 IO std::ostream& stream,
00865 OUT std::string& root
00866 )
00867 {
00868         perf::Timer timer("vgfx::setScript");
00869         ASSERT(map, "null");
00870         ASSERT(parent_id, "null");
00871         ASSERT(script_id, "null");
00872         ASSERT(stream.good(), "bad stream?");
00873 
00874         // the setScript() function will create a sub-object of the parent
00875         // (with the name parent_id_script_root) and a child object of that
00876         // (named parent_id_script_output) that is the executed contents of
00877         // the script.  The script_root contains all script parameters as
00878         // meta keys.
00879 
00880         // construct object IDs
00881         //   (root = script parameter group, out = script output)
00882         root = parent_id;       
00883         root += "ScriptRoot";
00884         const char * root_id = root.c_str();
00885 
00886         std::string out;
00887         getScriptOutputId(parent_id, out);
00888         const char * out_id = out.c_str();
00889 
00890         // if the script output object already exists, delete it
00891         if (map->findObject(out_id)) {
00892                 stream << "remove " << root_id << ".object.output\n";
00893                 stream << "remove " << out_id << "\n";
00894         }
00895 
00896         // if the script root object already exists, delete it
00897         if (map->findObject(root_id)) {
00898                 stream << "remove " << parent_id << ".object.scriptParams\n";
00899                 stream << "remove " << root << "\n";
00900         }
00901 
00902         // create empty group for script root
00903         stream << "add group {\n";
00904         stream << "id " << root_id << "\n";
00905         stream << "}\n";
00906 
00907         // get the function object
00908         Primitive * fn = map->findObject(script_id);
00909         ASSERT(fn, "Need to add function object to map first!  script='%s'",
00910             script_id);
00911 
00912         // get bounding rect of parent object
00913         Primitive * p = map->findObject(parent_id);
00914         ASSERT(p, "Parent does not exist? '%s'", parent_id);
00915         rect_t r;
00916         p->getBoundingRect(r);
00917 
00918         // get default parameters
00919         dictionary_t data;
00920         data["rootId"] = out_id;
00921         data["width"] = getStringValue(r.right - r.left);
00922         data["height"] = getStringValue(r.bottom - r.top);
00923 
00924         // get all public script parameters
00925         VecString params;
00926         VecString path;
00927         path.push_back("public");
00928         fn->listContainers(path, params);
00929         for (VecString::const_iterator i = params.begin(); i != params.end();
00930              ++i) {
00931                 const char * name = i->c_str();
00932                 // DPRINTF("  Adding public parameter '%s'...", name);
00933 
00934                 dictionary_t q;
00935                 path.clear();
00936                 path.push_back("public");
00937                 path.push_back(name);
00938                 fn->getContainerDictionary(path, q);
00939 
00940                 // add to public dictionary
00941                 // is this in the defaults?
00942                 const char * value = pset_def.getValue(name);
00943                 if (!value) {
00944                         // not in defaults, use default in script
00945                         value = getRequiredValue(q, "value");
00946                 }
00947                 data[name] = value;
00948 
00949                 // update script parameter group
00950                 stream << "set " << root << ".meta." << name << " " << name;
00951                 stream << " \"" << value << "\"\n";;
00952         }
00953 
00954         // invoke function to create script output object
00955         invokeFunction(fn, map, data, stream);
00956 
00957         // add output to script params
00958         stream << "set " << root << ".meta.parentId parentId ";
00959         stream << parent_id << "\n";
00960         stream << "set " << root << ".object.output id " << out << " z -1.0\n";
00961         stream << "set " << root << ".object.script id " << script_id << "\n";
00962 
00963         // add this to the parent
00964         stream << "set " << parent_id << ".object.scriptParams id " << root << " z -1.0\n";
00965 
00966         // that's it!
00967 }
00968 
00969 
00970 
00971 void
00972 updateScriptParameters
00973 (
00974 IN ObjectMap * map,
00975 IN const char * parent_id,
00976 IN const objtree::property_set_t& pset,
00977 IO std::ostream& stream
00978 )
00979 {
00980         perf::Timer timer("vgfx::updateScriptParameters");
00981         ASSERT(map, "null");
00982         ASSERT(parent_id, "null");
00983         ASSERT(stream.good(), "bad stream");
00984 
00985         // we are being asked to update script parameters.  This means
00986         // re-creating the script output
00987 
00988         // load the parent object (should be the script_params object)
00989         vgfx::Primitive * params = map->findObject(parent_id);
00990         ASSERT(params, "should have a valid script parameter object '%s'",
00991             parent_id);
00992 
00993         // get the parent of the script parameter object
00994         VecString path;
00995         path.push_back("meta");
00996         path.push_back("parentId");
00997         dictionary_t data;
00998         params->getContainerDictionary(path, data);
00999         const char * grandparent_id = getRequiredValue(data, "parentId");
01000         // DPRINTF("grandparent = '%s'", grandparent_id);
01001         vgfx::Primitive * gp = map->findObject(grandparent_id);
01002         ASSERT(gp, "Grandparent does not exist? '%s'", grandparent_id);
01003 
01004         // get bounding rect of grandparent
01005         rect_t r;
01006         gp->getBoundingRect(r);
01007 
01008         // what is the id of the script output object?
01009         std::string output_id;
01010         getScriptOutputId(grandparent_id, output_id);
01011 
01012         // set standard parameters
01013         dictionary_t script_params;
01014         script_params["rootId"] = output_id;
01015         script_params["width"] = getStringValue(r.right - r.left);
01016         script_params["height"] = getStringValue(r.bottom - r.top);
01017 
01018         // override standard parameters!
01019         if (pset.getValue("width"))
01020                 script_params["width"] = pset.getValue("width");
01021         if (pset.getValue("height"))
01022                 script_params["height"] = pset.getValue("height");
01023 
01024         // get script object
01025         path.clear();
01026         path.push_back("object");
01027         path.push_back("script");
01028         params->getContainerDictionary(path, data);
01029         const char * script_id = getRequiredValue(data, "id");
01030         // DPRINTF("Adjusting parameters for script '%s'", script_id);
01031         vgfx::Primitive * fn = map->findObject(script_id);
01032         ASSERT(fn, "No such script object found? '%s'", script_id);
01033 
01034         // get public script parameters
01035         path.clear();
01036         path.push_back("public");
01037         VecString props;
01038         fn->listContainers(path, props);
01039 
01040         // set based on property set
01041         for (VecString::const_iterator i = props.begin(); i != props.end();
01042              ++i) {
01043                 const char * name = i->c_str();
01044                 const char * value = pset.getValue(name);
01045                 ASSERT(value, "Script public parameter '%s' not found in pset?",
01046                     name);
01047                 // DPRINTF("  Setting public parameter '%s' = '%s'", name, value);
01048 
01049                 script_params[name] = value;
01050         }
01051 
01052         // clear existing script output object
01053         stream << "remove " << parent_id << ".object.output\n";
01054         stream << "remove " << output_id << "\n";
01055 
01056         // invoke function to regenerate script output
01057         invokeFunction(fn, map, script_params, stream);
01058 
01059         // set back to parent
01060         stream << "set " << parent_id << ".object.output id " << output_id << "\n";
01061 }
01062 
01063 
01064 
01065 void
01066 resizeScript
01067 (
01068 IN ObjectMap * map,
01069 IN const char * parent_id,
01070 IN float width,
01071 IN float height,
01072 IO std::ostream& stream
01073 )
01074 {
01075         ASSERT(map, "null");
01076         ASSERT(parent_id, "null");
01077         ASSERT(width > 0, "bad width: %f", width);
01078         ASSERT(height > 0, "bad height: %f", height);
01079         ASSERT(stream.good(), "bad stream");
01080 
01081         // look up parent
01082         Primitive * parent = map->findObject(parent_id);
01083         ASSERT(parent, "could not find script parent: '%s'", parent_id);
01084 
01085         // get script object
01086         const char * tag_path = "script";
01087         xform_2d_t T;   // identity
01088         visit_result_t vr;
01089         parent->getPrimitive(tag_path, T, vr);
01090         ASSERT(vr.p, "No script child for parent '%s'", parent_id);
01091         Primitive * script = vr.p;
01092 
01093         // get all required parameters
01094         VecString path;
01095         path.push_back("public");
01096         VecString params;
01097         script->listContainers(path, params);
01098 
01099         objtree::property_set_t pset;   // we'll populate this
01100 
01101         // pull properties from parent (should be a script_params object)
01102         for (VecString::iterator i = params.begin(); i != params.end(); ++i) {
01103                 const char * param = i->c_str();
01104                 DPRINTF("Looking for parameter '%s'...", param);
01105 
01106                 // get value from parent
01107                 path.clear();
01108                 path.push_back("meta");
01109                 path.push_back(param);
01110 
01111                 dictionary_t data;
01112                 parent->getContainerDictionary(path, data);
01113 
01114                 // add to property set
01115                 pset[param] =
01116                     objtree::property_t(getRequiredValue(data, param));
01117         }
01118 
01119         // add width, height
01120         pset["width"] = objtree::property_t(getStringValue(width).c_str());
01121         pset["height"] = objtree::property_t(getStringValue(height).c_str());
01122 
01123         // invoke other helper
01124         updateScriptParameters(map, parent_id, pset, stream);
01125 }
01126 
01127 
01128 
01129 };      // vgfx namespace
01130