00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "vgfx.h"
00036 #include "drawer.h"
00037
00038 #include <istream>
00039 #include <sstream>
00040
00041 #include "bezier/fit.h"
00042 #include "common/wave_ex.h"
00043 #include "objtree/objtree.h"
00044 #include "perf/perf.h"
00045 #include "util/parsing.h"
00046 #include "util/token_stream.h"
00047
00048
00049
00050
00051 namespace vgfx {
00052
00053
00054 static const eParseBehavior s_line_behavior = (eParseBehavior) (
00055 eParse_StripComments |
00056 eParse_StripBogus |
00057 eParse_RespectQuotes
00058 );
00059
00060
00061 enum eGroupType {
00062 eGT_Normal = 1,
00063 eGT_Path = 2,
00064
00065 eGT_Invalid = 0
00066 };
00067
00068
00069
00070 struct brush_entry_t {
00071 const char * key;
00072 eBrushAttribute attrib;
00073 };
00074
00075
00076 #define BRUSH_ENTRY(key, attrib) { "vgfx" #key , eBrush_ ##attrib },
00077
00078 static const brush_entry_t s_brushTable[] = {
00079 BRUSH_ENTRY(Color, PenColor)
00080 BRUSH_ENTRY(FillColor, FillColor)
00081 BRUSH_ENTRY(Thickness, PenThickness)
00082 BRUSH_ENTRY(Font, Font)
00083 BRUSH_ENTRY(TextFlags, TextFlags)
00084
00085
00086 { NULL, eBrush_Invalid }
00087 };
00088
00089
00090
00091 static dword_t
00092 getDwordFromPointer
00093 (
00094 IN void * ptr
00095 )
00096 throw()
00097 {
00098
00099 const dword_t * pdw = (const dword_t *) &ptr;
00100 return *pdw;
00101 }
00102
00103
00104
00105 static int
00106 getPathElement
00107 (
00108 IN const char * path,
00109 IO char * buffer,
00110 IN int bufsize
00111 )
00112 {
00113 ASSERT(path, "null path");
00114 ASSERT(buffer, "null");
00115 ASSERT(bufsize > 1, "bad buffer size: %d", bufsize);
00116
00117
00118 if (!strcmp("*", path)) {
00119 buffer[0] = '*';
00120 buffer[1] = 0;
00121 return 0;
00122 }
00123
00124
00125 const char * start = path;
00126 if ('/' == *path)
00127 ++path;
00128
00129
00130 int n = 0;
00131 while (n < bufsize && *path && '/' != *path) {
00132 buffer[n] = *path;
00133 ++path;
00134 ++n;
00135 }
00136
00137 if (n >= bufsize)
00138 return -1;
00139 buffer[n] = 0;
00140
00141 if ('/' == *path)
00142 ++path;
00143
00144 return path - start;
00145 }
00146
00147
00148
00149 static void
00150 updateBrush
00151 (
00152 IN const Dictionary& d,
00153 IN Drawer * drawer
00154 )
00155 {
00156 ASSERT(drawer, "null");
00157
00158 for (const brush_entry_t * p = s_brushTable; p->key; ++p) {
00159
00160 const char * value = getValue(d, p->key);
00161 if (value) {
00162 drawer->setBrushAttribute(p->attrib, value);
00163 }
00164 }
00165 }
00166
00167
00168
00169
00170 static void
00171 adjustRefCount
00172 (
00173 IN ObjectMap * map,
00174 IN const char * id,
00175 IN long delta
00176 )
00177 {
00178 ASSERT(map, "null map");
00179 ASSERT(id, "null id");
00180
00181 Primitive * p = map->findObject(id);
00182 if (!p) {
00183 if (delta > 0) {
00184 WAVE_EX(wex);
00185 wex << "Cannot reference object '" << id << "': it does not ";
00186 wex << "exist.";
00187 }
00188
00189
00190
00191 return;
00192 }
00193
00194 if (+1 == delta) {
00195 p->incrementRefCount();
00196 } else if (-1 == delta) {
00197 p->decrementRefCount();
00198 } else {
00199 ASSERT(false, "bad ref count delta: %ld", delta);
00200 }
00201 }
00202
00203
00204
00205 class ObjectRef {
00206 public:
00207
00208 ObjectRef(void) throw() : m_map(NULL), m_id("") { }
00209 ObjectRef(IN const ObjectRef& r) : m_map(NULL), m_id("") {
00210 *this = r;
00211 }
00212 ~ObjectRef(void) {
00213
00214 this->clear();
00215 }
00216
00217
00218 const char * getID(void) const throw() { return m_id.c_str(); }
00219 void setID(IN ObjectMap * map, IN const char * id) {
00220 ASSERT(map, "null");
00221 ASSERT(id, "null");
00222 this->clear();
00223 m_map = map;
00224 m_id = id;
00225 if (m_id != "") {
00226 adjustRefCount(m_map, id, +1);
00227 }
00228 }
00229 void dump(IN const char * title) const throw() {
00230 DPRINTF(" %s: map=%p id='%s'",
00231 title, m_map, m_id.c_str());
00232 }
00233
00234 const ObjectRef& operator= (IN const ObjectRef& r) {
00235 this->clear();
00236 if (r.m_map) {
00237 this->setID(r.m_map, r.getID());
00238 }
00239 return *this;
00240 }
00241
00242 private:
00243
00244 void clear(void) {
00245 if (m_map && m_id != "") {
00246 adjustRefCount(m_map, m_id.c_str(), -1);
00247 }
00248 m_map = NULL;
00249 m_id = "";
00250 }
00251
00252 ObjectMap *m_map;
00253 std::string m_id;
00254 };
00255
00256
00257 struct object_t {
00258 void dump(IN const char * title) const throw() {
00259 ref.dump(title);
00260 }
00261 const char * getTag(void) const throw() { return tag.c_str(); }
00262 void clear(void) throw() {
00263 x = y = z = phi = 0.0;
00264 scale = 1.0;
00265 xform.clear();
00266 }
00267
00268
00269 ObjectRef ref;
00270 std::string tag;
00271 float x;
00272 float y;
00273 float z;
00274 float phi;
00275 float scale;
00276 xform_2d_t xform;
00277 };
00278
00279
00280
00281
00282 typedef std::multimap<float, object_t> map_obj_t;
00283
00284
00285
00286
00287
00288
00289
00290
00291 static void
00292 updateObjectTransform
00293 (
00294 IO object_t& obj
00295 )
00296 {
00297
00298
00299 xform_2d_t translate;
00300 translate.setTranslate(obj.x, obj.y);
00301
00302 xform_2d_t zrotate;
00303 zrotate.setZRotate(obj.phi);
00304
00305 xform_2d_t temp;
00306 temp.setToProductOf(translate, zrotate);
00307
00308 xform_2d_t scale;
00309 scale.setIdentity();
00310 scale.scale(obj.scale);
00311
00312 obj.xform.setToProductOf(temp, scale);
00313 }
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324 class Group : public Primitive {
00325 public:
00326 Group(IN ObjectMap * map) throw();
00327 ~Group(void) throw();
00328
00329
00330 void addMeta(IN const char * key,
00331 IN const char * value);
00332 void addObject(IN const object_t& obj);
00333
00334
00335 virtual const char * getType(void) const throw() { return "group"; }
00336 virtual void persist(IO std::ostream& stream) const;
00337 virtual void listContainers(IN const VecString& path,
00338 OUT VecString& ids) const;
00339 virtual bool doesContainerExist(IN const VecString& path) const;
00340 virtual bool canCreateContainer(IN const VecString& path) const;
00341 virtual void removeContainer(IN const VecString& path);
00342 virtual void getContainerDictionary(IN const VecString& path,
00343 OUT dictionary_t& data) const;
00344 virtual void setContainerDictionary(IN const VecString& path,
00345 IN const dictionary_t& data);
00346 virtual void recalcBoundingRect(IN const char * tag_path,
00347 IN Drawer * drawer,
00348 IN const xform_2d_t& T) throw();
00349 virtual bool getBoundingRect(OUT rect_t& r) const throw();
00350 virtual bool getPrimitive(IN const char * tag_path,
00351 IN const xform_2d_t& T,
00352 OUT visit_result_t& vr);
00353 virtual void draw(IN Drawer * drawer,
00354 IN const rect_t& r_cm,
00355 IN const xform_2d_t& T);
00356 virtual bool visit(IN const rect_t& r,
00357 IN const xform_2d_t& T,
00358 IN const char * tag_path,
00359 IN callback_t callback,
00360 IN void * context,
00361 IN eHitDetect hit);
00362
00363 private:
00364
00365 map_obj_t::iterator findObject(IN const char * tag) throw();
00366 map_obj_t::const_iterator findObject(IN const char * tag) const throw();
00367 void updateDrawer(IN Drawer * drawer,
00368 IN const xform_2d_t& T);
00369 void expandPath(IN const char * path);
00370 void nukePath(void);
00371
00372
00373 map_obj_t m_objects;
00374 Dictionary m_meta;
00375 ObjectMap *m_map;
00376 rect_t m_bounding;
00377 eGroupType m_type;
00378 bool m_have_rect;
00379 };
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389 Group::Group
00390 (
00391 IN ObjectMap * map
00392 )
00393 throw() :
00394 m_map(map),
00395 m_type(eGT_Normal),
00396 m_have_rect(false)
00397 {
00398 }
00399
00400 Group::~Group(void)
00401 throw()
00402 {
00403 if (!m_map || eGT_Path != m_type)
00404 return;
00405
00406 this->nukePath();
00407 }
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 void
00418 Group::addMeta
00419 (
00420 IN const char * key,
00421 IN const char * value
00422 )
00423 {
00424 ASSERT(key, "null key passed to addMeta()");
00425 ASSERT(value, "null value passed to addMeta()");
00426
00427 ASSERT(key[0], "empty key passed to addMeta()");
00428
00429 m_meta[key] = value;
00430
00431
00432 if (!strcmp("vgfxPath", key)) {
00433 m_type = eGT_Path;
00434 this->nukePath();
00435 this->expandPath(value);
00436 }
00437 }
00438
00439
00440
00441 void
00442 Group::addObject
00443 (
00444 IN const object_t& obj
00445 )
00446 {
00447 const char * tag = obj.getTag();
00448 ASSERT(*tag, "null tag?");
00449
00450 if (!objtree::isValidPropertyName(tag)) {
00451 WAVE_EX(wex);
00452 wex << "Invalid object tag: '" << tag << "'";
00453 }
00454
00455 map_obj_t::iterator i = this->findObject(tag);
00456 if (m_objects.end() != i) {
00457 WAVE_EX(wex);
00458 wex << "Multiple group objects with the same tag: '" << tag;
00459 wex << "'";
00460 }
00461
00462 m_objects.insert(map_obj_t::value_type(obj.z, obj));
00463 }
00464
00465
00466
00467 static void
00468 writeXY
00469 (
00470 IO std::ostream& stream,
00471 IN float x,
00472 IN float y
00473 )
00474 {
00475 long x_l = (long)(1000.0 * x + 0.5);
00476 long y_l = (long)(1000.0 * y + 0.5);
00477
00478 stream << "x" << x_l << "y" << y_l;
00479 }
00480
00481
00482 #define PERSIST_VAL2(q,v) { if (obj.q) { stream << "\t" << #q << " " << (v); } }
00483 #define PERSIST_VAL(q) PERSIST_VAL2(q, obj.q)
00484
00485 void
00486 Group::persist
00487 (
00488 IO std::ostream& stream
00489 )
00490 const
00491 {
00492 perf::Timer timer("Group::persist");
00493
00494 stream << "group {\n";
00495 stream << "\tid\t" << this->getID() << "\n";
00496
00497 for (Dictionary::const_iterator i = m_meta.begin(); i != m_meta.end();
00498 ++i) {
00499 const char * name = i->first.c_str();
00500 const char * value = i->second.c_str();
00501
00502
00503 if (!strcmp("vgfxPath", name)) {
00504 continue;
00505 }
00506
00507 stream << "\tmeta\t" << name << " \"" << value << "\"\n";
00508 }
00509
00510
00511
00512 if (eGT_Path == m_type) {
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524 SetString ordered;
00525 for (map_obj_t::const_iterator i = m_objects.begin();
00526 i != m_objects.end(); ++i) {
00527 ordered.insert(i->second.ref.getID());
00528 }
00529
00530
00531 VecString null_path;
00532 dictionary_t data;
00533 float x0 = 0.0;
00534 float y0 = 0.0;
00535 stream << "\tmeta\tvgfxPath \"";
00536 for (SetString::iterator i = ordered.begin();
00537 i != ordered.end(); ++i) {
00538 const char * id = i->c_str();
00539
00540 Primitive * p = m_map->findObject(id);
00541 ASSERT(p, "bezier object not there '%s'", id);
00542 p->getContainerDictionary(null_path, data);
00543
00544 float x = atof(getRequiredValue(data, "x0"));
00545 float y = atof(getRequiredValue(data, "y0"));
00546
00547
00548 if (x != x0 || y != y0) {
00549 writeXY(stream, x0, y0);
00550 stream << "|g0|";
00551 }
00552 writeXY(stream, x, y);
00553 stream << "|";
00554
00555 x0 = atof(getRequiredValue(data, "x2"));
00556 y0 = atof(getRequiredValue(data, "y2"));
00557 }
00558
00559
00560 writeXY(stream, x0, y0);
00561 stream << "\"\n}";
00562 return;
00563 }
00564
00565
00566 for (map_obj_t::const_iterator i = m_objects.begin();
00567 i != m_objects.end(); ++i) {
00568 const object_t& obj = i->second;
00569 const char * tag = obj.getTag();
00570
00571 stream << "\tobject\ttag " << tag;
00572 stream << "\tid " << obj.ref.getID();
00573 PERSIST_VAL(x)
00574 PERSIST_VAL(y)
00575 PERSIST_VAL(z)
00576 if (1 != obj.scale) {
00577 PERSIST_VAL(scale)
00578 }
00579 PERSIST_VAL2(phi, 180.0 * obj.phi / M_PI);
00580 stream << "\n";
00581 }
00582
00583 stream << "}";
00584 }
00585
00586
00587
00588 void
00589 Group::listContainers
00590 (
00591 IN const VecString& path,
00592 OUT VecString& ids
00593 )
00594 const
00595 {
00596 ids.clear();
00597 if (path[0] == "meta") {
00598 for (Dictionary::const_iterator i = m_meta.begin();
00599 i != m_meta.end(); ++i) {
00600 ids.push_back(i->first);
00601 }
00602 } else if (path[0] == "object") {
00603 for (map_obj_t::const_iterator i = m_objects.begin();
00604 i != m_objects.end(); ++i) {
00605 ids.push_back(i->second.tag);
00606 }
00607 } else {
00608 WAVE_EX(wex);
00609 wex << "Unknown path for group: " << path[0];
00610 }
00611 }
00612
00613
00614
00615 bool
00616 Group::doesContainerExist
00617 (
00618 IN const VecString& path
00619 )
00620 const
00621 {
00622 if (path[0] == "meta") {
00623 return (m_meta.end() != m_meta.find(path[1]));
00624 } else if (path[0] == "object") {
00625 return (m_objects.end() != this->findObject(path[1].c_str()));
00626 }
00627 return false;
00628 }
00629
00630
00631 bool
00632 Group::canCreateContainer
00633 (
00634 IN const VecString& path
00635 )
00636 const
00637 {
00638 if (path[0] == "meta" || path[0] == "object")
00639 return true;
00640 return false;
00641 }
00642
00643
00644 void
00645 Group::removeContainer
00646 (
00647 IN const VecString& path
00648 )
00649 {
00650 if (path.size() > 2) {
00651 WAVE_EX(wex);
00652 wex << "Path is too large for group (" << path.size();
00653 wex << " elements)";
00654 }
00655
00656 if (path[0] == "meta") {
00657 Dictionary::iterator i = m_meta.find(path[1]);
00658 if (m_meta.end() == i) {
00659 WAVE_EX(wex);
00660 wex << "Cannot remove this meta data element '";
00661 wex << path[1] << "' since it does not exist.";
00662 }
00663 m_meta.erase(i);
00664 } else if (path[0] == "object") {
00665 map_obj_t::iterator i = this->findObject(path[1].c_str());
00666 if (m_objects.end() == i) {
00667 WAVE_EX(wex);
00668 wex << "Cannot remove this object '" << path[1];
00669 wex << "' since it does not exist.";
00670 }
00671 m_objects.erase(i);
00672 } else {
00673 WAVE_EX(wex);
00674 wex << "Unknown path in group: '" << path[0] << "'";
00675 }
00676 }
00677
00678
00679 void
00680 Group::getContainerDictionary
00681 (
00682 IN const VecString& path,
00683 OUT dictionary_t& data
00684 )
00685 const
00686 {
00687
00688
00689 if (path.size() > 2) {
00690 WAVE_EX(wex);
00691 wex << "path is too large for group (" << path.size();
00692 wex << " elements)";
00693 }
00694
00695 data.clear();
00696 if (path[0] == "meta") {
00697 const char * name = path[1].c_str();
00698 Dictionary::const_iterator i = m_meta.find(name);
00699 if (m_meta.end() == i) {
00700 WAVE_EX(wex);
00701 wex << "No meta data field with name '" << name;
00702 wex << "' exists in this group";
00703 }
00704 const char * value = i->second.c_str();
00705 data[name] = value;
00706 } else if (path[0] == "object") {
00707 map_obj_t::const_iterator i = this->findObject(path[1].c_str());
00708 if (m_objects.end() == i) {
00709 WAVE_EX(wex);
00710 wex << "No object with tag '" << path[1] << "' exists";
00711 wex << " in this group";
00712 }
00713 const object_t& obj = i->second;
00714 const char * tag = obj.getTag();
00715 data["tag"] = tag;
00716 data["id"] = obj.ref.getID();
00717 data["x"] = getStringValue(obj.x);
00718 data["y"] = getStringValue(obj.y);
00719 data["z"] = getStringValue(obj.z);
00720 data["scale"] = getStringValue(obj.scale);
00721 data["phi"] = getStringValue(180 * obj.phi / M_PI);
00722 } else {
00723 WAVE_EX(wex);
00724 wex << "Invalid path for dictionary retrieval: '" << path[0];
00725 wex << "'";
00726 }
00727 }
00728
00729
00730
00731 void
00732 Group::setContainerDictionary
00733 (
00734 IN const VecString& path,
00735 IN const dictionary_t& data
00736 )
00737 {
00738
00739
00740 if (path.size() > 2) {
00741 WAVE_EX(wex);
00742 wex << "Path is too long for group (" << path.size();
00743 wex << " elements)";
00744 }
00745
00746 if (path[0] == "meta") {
00747 const char * name = path[1].c_str();
00748 const char * value = getRequiredValue(data, name);
00749 this->addMeta(name, value);
00750 } else if (path[0] == "object") {
00751 map_obj_t::iterator i = this->findObject(path[1].c_str());
00752 if (m_objects.end() == i) {
00753
00754 object_t obj;
00755 const char * in_tag = path[1].c_str();
00756 const char * tag = getOptionalValue(data, "tag", in_tag);
00757 if (strcmp(tag, in_tag)) {
00758 WAVE_EX(wex);
00759 wex << "tag for new object '" << tag << "' ";
00760 wex << "does not match path '" << path[1];
00761 wex << "'";
00762 }
00763 const char * id = getValue(data, "id");
00764 ASSERT_THROW(id, "Cannot setContainerDictionary(): " <<
00765 "need to create new object, and no 'id' field " <<
00766 "exists on input");
00767 obj.ref.setID(m_map, id);
00768 obj.tag = tag;
00769 obj.x = atof(getOptionalValue(data, "x", "0.0"));
00770 obj.y = atof(getOptionalValue(data, "y", "0.0"));
00771 obj.z = atof(getOptionalValue(data, "z", "0.0"));
00772 obj.scale = atof(getOptionalValue(data, "scale", "1.0"));
00773 obj.phi = atof(getOptionalValue(data, "phi", "0.0"));
00774 obj.phi *= M_PI / 180.0;
00775 updateObjectTransform(obj);
00776 this->addObject(obj);
00777 } else {
00778
00779 object_t& obj = i->second;
00780 for (dictionary_t::const_iterator j = data.begin();
00781 j != data.end(); ++j) {
00782 const char * name = j->first.c_str();
00783 const char * value = j->second.c_str();
00784
00785 if (!strcmp("id", name)) {
00786 obj.ref.setID(m_map, value);
00787 } else if (!strcmp("x", name)) {
00788 obj.x = atof(value);
00789 } else if (!strcmp("y", name)) {
00790 obj.y = atof(value);
00791 } else if (!strcmp("z", name)) {
00792 obj.z = atof(value);
00793 } else if (!strcmp("scale", name)) {
00794 obj.scale = atof(value);
00795 } else if (!strcmp("phi", name)) {
00796 obj.phi = M_PI * atof(value) / 180.0;
00797 } else if (!strcmp("tag", name)) {
00798 if (path[1] != value) {
00799 WAVE_EX(wex);
00800 wex << "Cannot change tag ";
00801 wex << "value from '" << path[1];
00802 wex << "' to '" << value << "'";
00803 }
00804 } else {
00805 WAVE_EX(wex);
00806 wex << "Cannot set this field for a ";
00807 wex << "group object element: '";
00808 wex << name << "'";
00809 }
00810 }
00811 updateObjectTransform(obj);
00812 }
00813 } else {
00814 WAVE_EX(wex);
00815 wex << "Invalid path in group: '" << path[0] << "'";
00816 }
00817 }
00818
00819
00820
00821 void
00822 Group::recalcBoundingRect
00823 (
00824 IN const char * tag_path,
00825 IN Drawer * drawer,
00826 IN const xform_2d_t& T
00827 )
00828 throw()
00829 {
00830 ASSERT(tag_path, "null tag path");
00831 ASSERT(drawer, "null drawer");
00832 ASSERT(m_map, "null object map");
00833
00834
00835
00836 static const int s_tokSize = 256;
00837 char element[s_tokSize];
00838 int offset = getPathElement(tag_path, element, s_tokSize);
00839 ASSERT(offset >= 0, "Failed to get next path element: '%s'", tag_path);
00840 const char * next_path = tag_path + offset;
00841
00842
00843
00844 bool isWildcard = !strcmp("*", element);
00845
00846
00847 PushPopBrush ppb(drawer, getDwordFromPointer(this));
00848 this->updateDrawer(drawer, T);
00849
00850
00851
00852
00853 m_have_rect = false;
00854 rect_t r;
00855 r.left = r.top = r.right = r.bottom = 0.0;
00856
00857
00858 for (map_obj_t::const_iterator i = m_objects.begin();
00859 i != m_objects.end(); ++i) {
00860 const object_t& obj = i->second;
00861
00862
00863 Primitive * p = m_map->findObject(obj.ref.getID());
00864 ASSERT(p, "all required sub objects should exist");
00865
00866
00867 if (isWildcard || !strcmp(element, obj.getTag())) {
00868
00869 xform_2d_t subT;
00870 subT.setToProductOf(T, obj.xform);
00871
00872
00873
00874
00875
00876
00877 p->recalcBoundingRect(next_path, drawer, subT);
00878 }
00879
00880
00881 {
00882
00883
00884
00885
00886 rect_t child;
00887 if (!p->getBoundingRect(child)) {
00888
00889 continue;
00890 }
00891
00892
00893 rect_t xc;
00894 obj.xform.transformRect(child, xc);
00895
00896
00897 if (!m_have_rect) {
00898 r = xc;
00899 m_have_rect = true;
00900 } else {
00901 r.inflate(xc);
00902 }
00903
00904 }
00905 }
00906
00907
00908 const char * w = getValue(m_meta, "vgfxClipWidth");
00909 const char * h = getValue(m_meta, "vgfxClipHeight");
00910 if (w && h) {
00911 r.left = r.top = 0.0;
00912 r.right = atof(w);
00913 r.bottom = atof(h);
00914 m_have_rect = true;
00915 }
00916
00917 m_bounding = r;
00918
00919
00920 }
00921
00922
00923
00924 bool
00925 Group::getBoundingRect
00926 (
00927 OUT rect_t& r
00928 )
00929 const throw()
00930 {
00931 r = m_bounding;
00932 return m_have_rect;
00933 }
00934
00935
00936
00937 bool
00938 Group::getPrimitive
00939 (
00940 IN const char * tag_path,
00941 IN const xform_2d_t& T,
00942 OUT visit_result_t& vr
00943 )
00944 {
00945 ASSERT(tag_path, "null tag path");
00946
00947
00948
00949 static const int s_tokSize = 256;
00950 char element[s_tokSize];
00951 int offset = getPathElement(tag_path, element, s_tokSize);
00952 ASSERT(-1 != offset, "path element is too large");
00953
00954
00955 if (!*element) {
00956
00957 vr.tag_path = tag_path;
00958 vr.T = T;
00959 vr.p = this;
00960 return true;
00961 }
00962
00963 map_obj_t::iterator i = this->findObject(element);
00964 if (m_objects.end() == i) {
00965
00966 vr.clear();
00967 return false;
00968 }
00969
00970 object_t& obj = i->second;
00971
00972
00973 xform_2d_t newT;
00974 newT.setToProductOf(T, obj.xform);
00975
00976
00977 vgfx::Primitive * p = m_map->findObject(obj.ref.getID());
00978 ASSERT(p, "tag found but no primitive");
00979
00980
00981 if (*(tag_path + offset)) {
00982
00983 return p->getPrimitive(tag_path + offset, newT, vr);
00984 }
00985
00986
00987 vr.tag_path = tag_path;
00988 vr.T = newT;
00989 vr.p = p;
00990 return true;
00991 }
00992
00993
00994
00995 void
00996 Group::draw
00997 (
00998 IN Drawer * drawer,
00999 IN const rect_t& r_cm,
01000 IN const xform_2d_t& T
01001 )
01002 {
01003 ASSERT(drawer, "null drawer");
01004
01005
01006
01007
01008
01009
01010
01011
01012 if (drawer->isPrinter()) {
01013 const char * val = getValue(m_meta, "vgfxPrintable");
01014 if (val && !strcmp(val, "false")) {
01015 DPRINTF("'%s' has requested not to print",
01016 this->getID());
01017 return;
01018 }
01019 }
01020
01021
01022 PushPopBrush ppb(drawer, getDwordFromPointer(this));
01023 this->updateDrawer(drawer, T);
01024
01025
01026 for (map_obj_t::const_iterator i = m_objects.begin();
01027 i != m_objects.end(); ++i) {
01028 const object_t& obj = i->second;
01029
01030
01031
01032 Primitive * p = NULL;
01033 xform_2d_t subT;
01034 {
01035
01036
01037
01038
01039
01040
01041 p = m_map->findObject(obj.ref.getID());
01042 ASSERT(p, "all required sub objects should exist");
01043
01044
01045 subT.setToProductOf(T, obj.xform);
01046
01047
01048
01049 rect_t child;
01050 if (!p->getBoundingRect(child)) {
01051
01052 continue;
01053 }
01054
01055
01056 rect_t globalR;
01057 subT.transformRect(child, globalR);
01058
01059
01060
01061
01062
01063 if (!r_cm.intersects(globalR))
01064 continue;
01065 }
01066
01067
01068 p->draw(drawer, r_cm, subT);
01069 }
01070
01071
01072 const char * draw_bounds = getValue(m_meta, "vgfxDrawBounds");
01073 if (draw_bounds && !strcmp("true", draw_bounds)) {
01074 drawer->setTransform(T);
01075 drawer->fillRect(m_bounding);
01076 }
01077
01078
01079 drawer->endPath();
01080 }
01081
01082
01083
01084 bool
01085 Group::visit
01086 (
01087 IN const rect_t& r,
01088 IN const xform_2d_t& T,
01089 IN const char * tag_path,
01090 IN callback_t callback,
01091 IN void * context,
01092 IN eHitDetect hit
01093 )
01094 {
01095
01096 ASSERT(callback, "Group::visit() called with null callback");
01097 ASSERT(tag_path, "null tag path?");
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108 bool notify = true;
01109 if (eHit_Contained == hit) {
01110 rect_t me;
01111 this->getBoundingRect(me);
01112 rect_t xme;
01113 T.transformRect(me, xme);
01114 notify = r.contains(xme);
01115 }
01116
01117 if (notify) {
01118 visit_result_t vr;
01119 vr.p = this;
01120 vr.tag_path = tag_path;
01121 vr.T = T;
01122 if (!callback(context, vr))
01123 return false;
01124 }
01125
01126
01127 for (map_obj_t::const_iterator i = m_objects.begin();
01128 i != m_objects.end(); ++i) {
01129 const object_t& obj = i->second;
01130
01131
01132 bool recurse = true;
01133 xform_2d_t childT;
01134 Primitive * p = NULL;
01135 {
01136 perf::Timer timer("Group::visit--child xform");
01137
01138
01139 p = m_map->findObject(obj.ref.getID());
01140 ASSERT(p, "all subobjects should exist");
01141
01142
01143
01144 childT.setToProductOf(T, obj.xform);
01145
01146 if (eHit_Always != hit) {
01147
01148 rect_t childR;
01149 p->getBoundingRect(childR);
01150
01151
01152 rect_t globalR;
01153 childT.transformRect(childR, globalR);
01154
01155
01156
01157 recurse = r.intersects(globalR);
01158 }
01159 }
01160
01161 if (recurse) {
01162 ASSERT(p, "null node for recursion?");
01163
01164 std::string new_path = tag_path;
01165 new_path += "/";
01166 new_path += obj.getTag();
01167 if (!p->visit(r, childT, new_path.c_str(), callback,
01168 context, hit)) {
01169 return false;
01170 }
01171 }
01172 }
01173
01174
01175 return true;
01176 }
01177
01178
01179 map_obj_t::iterator
01180 Group::findObject
01181 (
01182 IN const char * tag
01183 )
01184 throw()
01185 {
01186 ASSERT(tag, "null");
01187
01188 for (map_obj_t::iterator i = m_objects.begin(); i != m_objects.end();
01189 ++i) {
01190 object_t& obj = i->second;
01191 if (obj.tag == tag) {
01192 return i;
01193 }
01194 }
01195
01196 return m_objects.end();
01197 }
01198
01199
01200
01201 map_obj_t::const_iterator
01202 Group::findObject
01203 (
01204 IN const char * tag
01205 )
01206 const throw()
01207 {
01208 ASSERT(tag, "null");
01209
01210 for (map_obj_t::const_iterator i = m_objects.begin();
01211 i != m_objects.end(); ++i) {
01212 const object_t& obj = i->second;
01213 if (obj.tag == tag) {
01214 return i;
01215 }
01216 }
01217
01218 return m_objects.end();
01219 }
01220
01221
01222
01223 void
01224 Group::updateDrawer
01225 (
01226 IN Drawer * drawer,
01227 IN const xform_2d_t& T
01228 )
01229 {
01230
01231
01232 ASSERT(drawer, "null");
01233
01234
01235 drawer->setTransform(T);
01236
01237
01238 updateBrush(m_meta, drawer);
01239
01240
01241 const char * w = getValue(m_meta, "vgfxClipWidth");
01242 const char * h = getValue(m_meta, "vgfxClipHeight");
01243 if (w && h) {
01244 drawer->setClipRect(0.0, 0.0, atof(w), atof(h));
01245 }
01246 }
01247
01248
01249
01250 void
01251 Group::expandPath
01252 (
01253 IN const char * path
01254 )
01255 {
01256 ASSERT(path, "null");
01257
01258
01259
01260
01261 typedef std::vector<point_t> vec_pt_t;
01262 vec_pt_t strokes;
01263 const char * p = path;
01264 point_t pt;
01265 bool is_gap = false;
01266 int id_ctr = 0;
01267 while (*p) {
01268
01269 char a = *p;
01270 ++p;
01271
01272
01273 long l = atol(p);
01274 float f = 0.001 * l;
01275
01276
01277 if ('-' == *p) { ++p; }
01278 while (*p && isdigit(*p)) { ++p; }
01279
01280
01281 if ('x' == a)
01282 pt.x = f;
01283 if ('y' == a)
01284 pt.y = f;
01285 if ('g' == a)
01286 is_gap = true;
01287
01288
01289 if (*p && '|' != *p)
01290 continue;
01291
01292
01293 if (!is_gap) {
01294
01295 strokes.push_back(pt);
01296 }
01297
01298 if (!is_gap && *p)
01299 continue;
01300
01301
01302
01303 if (strokes.size() < 2) {
01304 continue;
01305 }
01306
01307
01308 float ds = 1.0;
01309 bezier::quad_path_t b_path;
01310 bezier::getQuadPathFromRawPoints(&strokes[0], strokes.size(),
01311 ds, b_path);
01312
01313
01314 for (bezier::quad_path_t::iterator i = b_path.begin();
01315 i != b_path.end(); ++i) {
01316 const bezier::quad_bezier_t& qb = *i;
01317
01318
01319 init_quad_t iq;
01320 iq.id = this->getID();
01321 iq.id += "K";
01322 iq.id += getStringValue(id_ctr);
01323 ++id_ctr;
01324 iq.quad = qb;
01325 smart_ptr<Primitive> quad = create_quad(iq);
01326 ASSERT(quad, "failed to create new quad bezier");
01327
01328
01329 m_map->addObject(quad);
01330
01331
01332 object_t obj;
01333 obj.clear();
01334 obj.ref.setID(m_map, iq.id.c_str());
01335 obj.tag = iq.id;
01336 this->addObject(obj);
01337 }
01338 strokes.clear();
01339 is_gap = false;
01340 }
01341 }
01342
01343
01344
01345 void
01346 Group::nukePath
01347 (
01348 void
01349 )
01350 {
01351 ASSERT(eGT_Path == m_type, "only valid for path objects!");
01352
01353
01354 VecString ids;
01355 for (map_obj_t::iterator i = m_objects.begin(); i != m_objects.end();
01356 ++i) {
01357 const char * id = i->second.ref.getID();
01358 ids.push_back(id);
01359 }
01360 m_objects.clear();
01361 for (VecString::iterator i = ids.begin(); i != ids.end(); ++i) {
01362 const char * id = i->c_str();
01363
01364 m_map->removeObject(id);
01365 }
01366 }
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376 static void
01377 constructObject
01378 (
01379 IN ObjectMap * map,
01380 IN const dictionary_t& data,
01381 OUT object_t& obj
01382 )
01383 {
01384 ASSERT(map, "null");
01385
01386 obj.tag = getRequiredValue(data, "tag");
01387 obj.ref.setID(map, getRequiredValue(data, "id"));
01388 obj.x = atof(getOptionalValue(data, "x", "0.0"));
01389 obj.y = atof(getOptionalValue(data, "y", "0.0"));
01390 obj.z = atof(getOptionalValue(data, "z", "0.0"));
01391 obj.scale = atof(getOptionalValue(data, "scale", "1.0"));
01392
01393 obj.phi = atof(getOptionalValue(data, "phi", "0.0"));
01394 obj.phi = M_PI * obj.phi / 180.0;
01395
01396 updateObjectTransform(obj);
01397 }
01398
01399
01400
01401 smart_ptr<Primitive>
01402 parseGroup
01403 (
01404 IN std::istream& stream,
01405 IO ObjectMap * map
01406 )
01407 {
01408 perf::Timer timer("parseGroup");
01409
01410 ASSERT(map, "null object map passed to parseGroup()");
01411
01412 smart_ptr<Group> group = new Group(map);
01413 ASSERT(group, "out of memory");
01414
01415 std::string line, token, value;
01416
01417
01418 while (true) {
01419 line = getNextLineFromStream(stream, s_line_behavior);
01420 const char * cursor =
01421 getNextTokenFromString(line.c_str(), token, eParse_None);
01422
01423
01424 if ("" == token) {
01425 continue;
01426 }
01427
01428 if ("id" == token) {
01429 getNextTokenFromString(cursor, value, eParse_RespectQuotes);
01430 group->setID(value.c_str());
01431 } else if ("meta" == token) {
01432 std::string name;
01433 cursor = getNextTokenFromString(cursor, name, eParse_None);
01434 cursor = getNextTokenFromString(cursor, value, eParse_RespectQuotes);
01435
01436 group->addMeta(name.c_str(), value.c_str());
01437 } else if ("object" == token) {
01438 dictionary_t dict;
01439 getDictionaryFromString(cursor, "object", dict);
01440
01441 object_t obj;
01442 constructObject(map, dict, obj);
01443
01444
01445
01446 group->addObject(obj);
01447 } else if ("}" == token) {
01448 break;
01449 } else {
01450 WAVE_EX(wex);
01451 wex << "Unknown line keyword in group: '" << token;
01452 wex << "'";
01453 }
01454 }
01455
01456 return group;
01457 }
01458
01459
01460 };
01461