Commit cf3795c8 authored by David Thompson's avatar David Thompson

Many changes related to descriptive phrase views.

+ Implement `PhraseModel::handleCreated()`.
+ Hold expunged model entities rather than UUIDs.
  Change `smtk::model::Resource::deleteEntities()` so that the expunged
  container holds `Entity` shared-pointers rather than UUIDs;
  this keeps the entity records from going out of scope as they are
  removed from the model resource (long enough that the operation result
  can be processed to remove them from the UI).
+ Properly remove phrases of deleted components.
  This also removes overrides that ResourcePhraseModel provided for
  handling operation results; the base class implementation should
  do what is needed without further work by the subclass.
+ Provide a flatter (two-level) resource tree.
+ Documentation for views and phrases.
+ Improve sorting of descriptive phrases
  by comparing smtk::resource::Component subclasses and
  model entity types.
+ Show a valid icon for lists, not the invalid icon.
+ Eliminate some warnings from other parts of SMTK.
+ Make subphrase generator a user preference in the ParaView extensions.
parent 807f7526
......@@ -86,15 +86,63 @@ if (SPHINX_FOUND)
set(SMTK_USERGUIDE_DOCS
index.rst
userguide/index.rst
userguide/mesh/concepts.rst
userguide/mesh/IO.rst
userguide/operation/index.rst
userguide/operation/operators.rst
userguide/extension/index.rst
userguide/extension/paraview/index.rst
userguide/extension/paraview/representations.rst
userguide/extension/paraview/widgets.rst
userguide/extension/paraview/plugins.rst
userguide/extension/paraview/panels.rst
userguide/extension/paraview/lifecycle.rst
userguide/extension/paraview/anatomy.rst
userguide/extension/vtk.rst
userguide/extension/qt.rst
userguide/attribute/index.rst
userguide/attribute/concepts.rst
userguide/attribute/file-syntax.rst
userguide/attribute/concepts.rst
userguide/contributing.rst
userguide/administration.rst
userguide/obtain-build-install.rst
userguide/simulation/index.rst
userguide/overview.rst
userguide/workflow/index.rst
userguide/workflow/concepts.rst
userguide/model/index.rst
userguide/model/concepts.rst
userguide/model/sessions.rst
userguide/model/property-names.rst
userguide/contributing.rst
userguide/model/sessions.rst
userguide/model/session-polygon.rst
userguide/model/user-interface.rst
userguide/model/concepts.rst
userguide/model/operators.rst
userguide/model/session-cgm.rst
userguide/model/session-discrete.rst
userguide/model/session-exodus.rst
userguide/view/index.rst
userguide/view/concepts.rst
userguide/view/views.rst
userguide/view/phrases.rst
userguide/bindings/index.rst
userguide/bindings/generating-pybind11-bindings.rst
userguide/bindings/customizing-pybind11-bindings.rst
userguide/resource/index.rst
userguide/resource/concepts.rst
tutorials/index.rst
tutorials/add_a_session/index.rst
tutorials/add_a_session/operators.rst
tutorials/add_a_session/transcribing.rst
tutorials/add_a_session/troubleshooting.rst
tutorials/add_a_session/session_subclass.rst
tutorials/add_a_session/entity_uuids.rst
tutorials/python_first_steps/index.rst
tutorials/cxx_first_steps/index.rst
tutorials/implement_an_operator/index.rst
tutorials/debugging_python_tests/index.rst
)
if (DOXYGEN_FOUND)
# Need doxygen docs built if possible
......
## Phrase models
Several changes have been made to SMTK's descriptive phrase infrastructure:
+ A new subphrase generator that presents a fixed-depth tree
is available and is the default.
+ Changes due to operations are now handled properly by the
new subphrase generator but not by others.
+ There is a new phrase model, SelectionPhraseModel, that
monitors a selection and populates the top-level phrases
with selected objects.
### Developer-facing changes
Phrase models now properly listen for and respond to operation
manager events. When an operation completes, the phrase model
should respond by updating the list of top-level phrases and
then ask the top-level subphrase generator for a list of new
phrases to insert — along with the paths at which they belong.
The top-level subphrase generator may ask other subphrase
generators to add to this list.
Deleted and modified components are handled automatically by
the base phrase model class, but created components may appear
anywhere in the tree; the work of determining where to place
these phrases must be left to other classes.
## User-facing changes
The resource panel now presents the new two-level subphrase
generator.
......@@ -78,7 +78,7 @@ deserializing all of the attributes, definitions, items, and item-definitions
stored in an attribute resource.
Interfaces to the attribute resource
----------------------------------
------------------------------------
The attribute resource has three interfaces:
......
.. _smtk-resource-sys:
.. _smtk-extension-sys:
-----------------
SMTK's Extensions
......
......@@ -41,8 +41,10 @@ simulation domain.
model/index.rst
mesh/index.rst
simulation/index.rst
bindings/index.rst
view/index.rst
extension/index.rst
workflow/index.rst
bindings/index.rst
administration.rst
contributing.rst
......
Key Concepts
============
There are 2 ways that presentation is abstracted in SMTK:
views and trees of descriptive phrases.
:smtk:`View <smtk::view::View>`
instances are containers that hold information used to configure a view.
There are currently 2 types of views in SMTK:
* attribute views, which allow users to inspect and edit an entire attribute resource; and
* phrase views, which allow users to inspect and edit any resource by interacting with
a tree of one-line summary information related to a resource or component.
:smtk:`DescriptivePhrase <smtk::view::DescriptivePhrase>`
instances represent a single piece of information that can be presented
with a title string and an optional subtitle string.
Phrases may be queried for a related resource, component, color, visibility,
property name/value/type, and — most importantly — a list of child phrases
that provide further details.
Consider an example where we wish to present information about a model face.
The face itself could be a phrase whose title is the user-provided name of
the face, whose subtitle might indicate whether the face is planar or curved,
whose color and visibility reflect user choices for display in a render window,
and whose child phrases could include details about the face such as
(1) a list of edges bounding the face,
(2) properties defined on the face such as user annotation, and
(3) attributes associated with the face such as boundary conditions.
Developers may wish all or none of this information to be editable.
Configuration information specifying how phrases should be arranged
and what portions should be editable is held in a
:smtk:`View <smtk::view::View>` instance, since a view will hold
the top of the phrase tree.
Besides views and descriptive phrases,
there are 2 important presentation tools that SMTK provides in its view system:
:smtk:`Selection <smtk::view::Selection>`
instances hold a map from persistent objects to an integer value
representing the "level" or "type" of selection that the object
is participating in. For example, an object may be highlighted
as a pointer hovers over it or selected more permanently.
Different integer values indicate which (or both, if the integer
is interpreted as a bit vector) of these types of selections an
object belongs to.
:smtk:`AvailableOperators <smtk::view::AvailableOperators>`
instances provide a list of editing operations that a user may perform.
This list may vary based on the workflow as well as the current
selection.
The following sections discuss portions of the view system in depth.
.. _smtk-view-sys:
------------------
SMTK's View System
------------------
One of the main functions of SMTK is to present attributes, models, and meshes
to users so that they can edit and annotate them.
The view system provides classes that aid with this task without depending
on any particular user interface technology (Qt, HTML+JavaScript, VTK, etc.).
.. toctree::
:maxdepth: 4
concepts.rst
views.rst
phrases.rst
Hierarchical Phrases
====================
Descriptive phrase trees may be flat or deeply nested.
They are flexible and may be configured to present the
same resources and components in many different ways,
while accommodating changes to those resources and components
in response to editing operations.
In order to deal with this complexity, the duties of phrases
are split across several classes:
* Phrase models are responsible for determining which
descriptive phrases belong at the root of the tree.
They also provide utilities used to
* watch for events that may change the tree and
* determine how to notify user interfaces of those
changes in terms of deleted, added, re-ordered, and
modified phrases.
* Descriptive phrases are only responsible for storing
child phrases and, optionally, a reference to a
subphrase generator that can create or update
the list of child phrases on demand.
They also own a reference to another class
responsible for the phrase's content (i.e., title,
subtitle, and other related information).
* Subphrase generators create or update a list of
child phrases when given a reference to a potential
parent phrase.
* Phrase content classes own a reference to a persistent
object and determine what about that object should be
presented (i.e., they determine the topic of the phrase).
One content class may hold a model face and present the face's
name as the phrase title/topic, while another class may hold
the same face but present one of the face's properties
as its topic.
Phrase content classes may optionally reference another
phrase content instance, which they decorate.
For example, the :smtk:`VisibilityContent` class holds
a reference to a :smtk:`ResourcePhraseContent`
or :smtk:`ComponentPhraseContent` and simply overrides
the visibility of the underlying resource or component
with an application-specific visibility.
Thus, each descriptive phrase holds a reference to the head
of a singly-linked list of :smtk:`PhraseContent` instances
that decorate the instance at the tail of the list.
Phrase Models
-------------
.. todo:: Describe the base phrase model class
There are two phrase model subclasses:
:smtk:`ResourcePhraseModel <smtk::view::ResourcePhraseModel>`,
which lists resources matching a given set of filters at its top level.
It is used by the :smtk:`pqSMTKResourcePanel` class to present
a list of resources that have been loaded.
:smtk:`ComponentPhraseModel <smtk::view::ComponentPhraseModel>`,
which lists components matching a given set of filters at its top level.
It is used by the :smtk:`qtComponentItem` class to present
components that may be selected by a user in order to populate some item.
Phrase Content Types
--------------------
.. todo:: Describe the base phrase content class and its children
Subphrase Generators
--------------------
.. todo:: Describe the base subphrase generator class and its children
Views
=====
As mentioned in the previous section, views may show
editable attribute resources
or
trees of descriptive phrases.
Configuration information is different for each of these types.
Attribute views
---------------
This view information is used to configure :smtk:`qtBaseView` instances.
Views of an attribute resource may need to display many attributes, some of
which may be created upon user input (i.e., by adding a new material or boundary
condition).
However, there must be one view marked as the top-level view for any given
attribute resource.
This view may have child views that it uses to organize
(1) fixed, one-per-simulation attribute instances as well as
(2) attribute instances that may vary in number to fit the needs of the simulation.
Phrase views
------------
These views appear in 2 types of widgets in SMTK:
panels holding tree views (such as the resource panel) and
widgets for selecting components or resources, where only
a flat list is presented.
......@@ -240,8 +240,6 @@ void GirderFileBrowserFetcher::getCollectionsFolderInformation()
void GirderFileBrowserFetcher::finishGettingSecondLevelFolderInformation(
const QString& type, const QMap<QString, QString>& map)
{
int numItems = map.keys().size();
// Let's sort these by name. QMap sorts by key.
QMap<QString, QString> sortedByName;
for (const auto& id : map.keys())
......
......@@ -64,6 +64,16 @@ pqSMTKResource::pqSMTKResource(
pqSMTKResource::~pqSMTKResource()
{
/* FIXME:
* This causes a segfault on exit because the pqSMTKWrapper has been
* deleted out from under us. However, if the resource is deleted
* _before_ exit, then not removing the observer can cause a segfault
* later when an operation is performed.
auto behavior = pqSMTKBehavior::instance();
auto rsrcMgr = behavior->resourceManagerForServer(this->getServer());
rsrcMgr->smtkOperationManager()->observers().erase(m_key);
*/
QObject::disconnect(
this, SIGNAL(dataUpdated(pqPipelineSource*)), this, SLOT(synchronizeResource()));
......
......@@ -27,6 +27,7 @@
#include "smtk/view/DescriptivePhrase.h"
#include "smtk/view/ResourcePhraseModel.h"
#include "smtk/view/SubphraseGenerator.h"
#include "smtk/view/TwoLevelSubphraseGenerator.h"
#include "smtk/view/VisibilityContent.h"
#include "smtk/model/Entity.h"
......@@ -59,6 +60,7 @@ public:
: m_selnSource("resource panel")
, m_selnLabel("selected")
, m_hoverLabel("hovered")
, m_resourceTreeStyle(vtkSMTKSettings::HierarchicalStyle)
{
}
......@@ -205,6 +207,7 @@ public:
std::string m_selnLabel;
std::string m_hoverLabel;
std::map<smtk::common::UUID, int> m_visibleThings;
int m_resourceTreeStyle; // Which subphrase generator should be used?
};
pqSMTKResourcePanel::pqSMTKResourcePanel(QWidget* parent)
......@@ -212,8 +215,6 @@ pqSMTKResourcePanel::pqSMTKResourcePanel(QWidget* parent)
{
m_p = new Internal;
m_p->setup(this);
auto spg = smtk::view::SubphraseGenerator::create();
this->setPhraseGenerator(spg);
auto smtkBehavior = pqSMTKBehavior::instance();
// Listen for resources on current connections:
......@@ -257,7 +258,6 @@ void pqSMTKResourcePanel::setPhraseGenerator(smtk::view::SubphraseGeneratorPtr s
spg->setModel(m_p->m_phraseModel);
}
root->setDelegate(spg);
m_p->m_model->rebuildSubphrases(QModelIndex());
}
void pqSMTKResourcePanel::leaveEvent(QEvent* evt)
......@@ -563,6 +563,30 @@ void pqSMTKResourcePanel::updateSettings()
m_p->m_view, SIGNAL(entered(const QModelIndex&)), this, SLOT(hoverRow(const QModelIndex&)));
this->resetHover();
}
int resourceTreeStyle = smtkSettings->GetResourceTreeStyle();
if (resourceTreeStyle != m_p->m_resourceTreeStyle)
{
smtk::view::SubphraseGenerator::Ptr spg = nullptr;
switch (resourceTreeStyle)
{
case vtkSMTKSettings::HierarchicalStyle:
spg = smtk::view::SubphraseGenerator::create();
break;
case vtkSMTKSettings::TwoLevelStyle:
spg = smtk::view::TwoLevelSubphraseGenerator::create();
break;
default:
smtkWarningMacro(
smtk::io::Logger::instance(), "Unsupported resource tree style. Resetting to default.");
spg = smtk::view::SubphraseGenerator::create();
break;
}
if (spg)
{
m_p->m_resourceTreeStyle = resourceTreeStyle;
this->setPhraseGenerator(spg);
}
}
}
void pqSMTKResourcePanel::editObjectColor(const QModelIndex& idx)
......
......@@ -1836,6 +1836,39 @@
<BooleanDomain name="bool" />
</IntVectorProperty>
<IntVectorProperty
name="ResourceTreeStyle"
default_values="hierarchical"
command="SetResourceTreeStyle"
information_property="ResourceTreeStyleInfo"
panel_visibility="default">
<Documentation>
By default, resources are presented in a hierarchical fashion:
only the top-level components appear beneath a resource.
However, they may be expanded to list their children if they
have any.
For model entities, top-level components are those which do
not serve as geometric boundaries for any others (if they are
cells) or groups that are not members of any other group (if
they are groups) or models that are not owned by any other
model (if they are not models).
The alternative is a flatter, two-level hierarchy where,
underneath each resource is a list of entity types. Each
list of entity types has children that are the given type.
</Documentation>
<EnumerationDomain name="enum">
<Entry text="hierarchical" value="0"/>
<Entry text="two-level" value="1"/>
</EnumerationDomain>
</IntVectorProperty>
<IntVectorProperty
name="ResourceTreeStyleInfo"
command="GetResourceTreeStyle"
information_only="1">
<SimpleIntInformationHelper />
</IntVectorProperty>
<Hints>
<UseDocumentationForLabels />
......
......@@ -447,9 +447,10 @@ bool vtkSMTKModelRepresentation::ApplyDefaultStyle(
attr = item.first->as<smtk::attribute::Attribute>();
if (attr)
{
if (attr->associations())
auto assoc = attr->associations();
if (assoc)
{
for (auto obj : *attr->associations())
for (auto obj : *assoc)
{
atLeastOneSelected |=
self->SelectComponentFootprint(obj, /*selnBit TODO*/ 1, renderables);
......
......@@ -23,6 +23,7 @@ vtkSMTKSettings* vtkSMTKSettings::New()
vtkSMTKSettings::vtkSMTKSettings()
: HighlightOnHover(true)
, ResourceTreeStyle(HierarchicalStyle)
{
}
......@@ -34,6 +35,7 @@ void vtkSMTKSettings::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "HighlightOnHover: " << this->HighlightOnHover << "\n";
os << indent << "ResourceTreeStyle: " << this->ResourceTreeStyle << "\n";
}
vtkSMTKSettings* vtkSMTKSettings::GetInstance()
......
......@@ -38,10 +38,20 @@ public:
vtkGetMacro(HighlightOnHover, bool);
vtkSetMacro(HighlightOnHover, bool);
/**\brief Choose how resources should be presented.
*
* See the server-manager XML for details.
*/
vtkGetMacro(ResourceTreeStyle, int);
vtkSetMacro(ResourceTreeStyle, int);
static constexpr int HierarchicalStyle = 0; // Match value in smconfig.xml
static constexpr int TwoLevelStyle = 1; // Match value in smconfig.xml
protected:
vtkSMTKSettings();
bool HighlightOnHover;
int ResourceTreeStyle;
private:
vtkSMTKSettings(const vtkSMTKSettings&) = delete;
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 63.999999 63.999999"
id="svg8069"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="list_b.svg"
inkscape:export-filename="list_b@2x.png"
inkscape:export-xdpi="160"
inkscape:export-ydpi="160">
<defs
id="defs8071" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.4375"
inkscape:cx="32"
inkscape:cy="32"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1021"
inkscape:window-height="662"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="0" />
<metadata
id="metadata8074">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-988.3622)">
<g
id="g12608"
transform="translate(0,6)">
<rect
ry="2.48546"
rx="0"
y="999.3913"
x="9.1650505"
height="8"
width="10.097084"
id="rect12549"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect12551"
width="27.184458"
height="8"
x="27.650488"
y="999.3913"
rx="0"
ry="2.48546" />
</g>
<g
transform="translate(0,20)"
id="g12612">
<rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect12614"
width="10.097084"
height="8"
x="9.1650505"
y="999.3913"
rx="0"
ry="2.48546" />
<rect
ry="2.48546"
rx="0"
y="999.3913"
x="27.650488"
height="8"
width="27.184458"
id="rect12616"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
id="g12618"
transform="translate(0,34)">
<rect
ry="2.48546"
rx="0"
y="999.3913"
x="9.1650505"
height="8"
width="10.097084"
id="rect12620"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect12622"
width="27.184458"
height="8"
x="27.650488"
y="999.3913"
rx="0"
ry="2.48546" />
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 64 63.999999"
id="svg8069"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="list_w.svg"
inkscape:export-filename="list_w.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45">
<defs
id="defs8071" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.5431093"
inkscape:cx="40.086571"
inkscape:cy="32"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1021"
inkscape:window-height="662"
inkscape:window-x="0"
inkscape:window-y="1"
inkscape:window-maximized="0" />
<metadata
id="metadata8074">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-988.3622)">
<g
id="g12608"
transform="translate(0,6)"
style="fill:#ffffff;fill-opacity:1">
<rect
ry="2.48546"
rx="0"
y="999.3913"
x="9.1650505"
height="8"
width="10.097084"
id="rect12549"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect12551"
width="27.184458"
height="8"
x="27.650488"
y="999.3913"
rx="0"
ry="2.48546" />
</g>
<g
transform="translate(0,20)"
id="g12612"
style="fill:#ffffff;fill-opacity:1">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:12;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect12614"
width="10.097084"
height="8"
x="9.1650505"
y="999.3913"
rx="0"
ry="2.48546" />
<rect
ry="2.48546"
rx="0"
y="999.3913"