<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:"Segoe UI Emoji";
        panose-1:2 11 5 2 4 2 4 2 2 3;}
@font-face
        {font-family:Consolas;
        panose-1:2 11 6 9 2 2 4 3 2 4;}
@font-face
        {font-family:"Courier New \;color\:\#222222";
        panose-1:0 0 0 0 0 0 0 0 0 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
pre
        {mso-style-priority:99;
        mso-style-link:"HTML Preformatted Char";
        margin:0in;
        font-size:10.0pt;
        font-family:"Courier New";}
p.msonormal0, li.msonormal0, div.msonormal0
        {mso-style-name:msonormal;
        mso-margin-top-alt:auto;
        margin-right:0in;
        mso-margin-bottom-alt:auto;
        margin-left:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
p.m6872490181044312902msolistparagraph, li.m6872490181044312902msolistparagraph, div.m6872490181044312902msolistparagraph
        {mso-style-name:m_6872490181044312902msolistparagraph;
        mso-margin-top-alt:auto;
        margin-right:0in;
        mso-margin-bottom-alt:auto;
        margin-left:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
span.HTMLPreformattedChar
        {mso-style-name:"HTML Preformatted Char";
        mso-style-priority:99;
        mso-style-link:"HTML Preformatted";
        font-family:Consolas;}
span.m6872490181044312902ansi-red-fg
        {mso-style-name:m_6872490181044312902ansi-red-fg;}
span.m6872490181044312902apple-converted-space
        {mso-style-name:m_6872490181044312902apple-converted-space;}
span.m6872490181044312902pre
        {mso-style-name:m_6872490181044312902pre;}
span.m6872490181044312902ansi-green-fg
        {mso-style-name:m_6872490181044312902ansi-green-fg;}
span.m6872490181044312902ansi-cyan-fg
        {mso-style-name:m_6872490181044312902ansi-cyan-fg;}
span.m6872490181044312902ansi-blue-fg
        {mso-style-name:m_6872490181044312902ansi-blue-fg;}
span.m6872490181044312902ansi-green-intense-fg
        {mso-style-name:m_6872490181044312902ansi-green-intense-fg;}
span.EmailStyle30
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:20472936;
        mso-list-template-ids:1644713996;}
@list l0:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l0:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1
        {mso-list-id:48234779;
        mso-list-template-ids:550814072;}
@list l1:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l1:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2
        {mso-list-id:66152899;
        mso-list-template-ids:1716791132;}
@list l2:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l2:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l3
        {mso-list-id:265039431;
        mso-list-template-ids:321802988;}
@list l3:level1
        {mso-level-start-at:8;
        mso-level-number-format:alpha-lower;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level3
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level4
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level6
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level7
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l3:level9
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l4
        {mso-list-id:453325843;
        mso-list-template-ids:-265286698;}
@list l4:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l4:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5
        {mso-list-id:520513940;
        mso-list-template-ids:1374826668;}
@list l5:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l5:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6
        {mso-list-id:581139725;
        mso-list-template-ids:1023053830;}
@list l6:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l6:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l7
        {mso-list-id:585961155;
        mso-list-template-ids:-461333330;}
@list l7:level1
        {mso-level-start-at:6;
        mso-level-number-format:alpha-lower;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level3
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level4
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level6
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level7
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l7:level9
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l8
        {mso-list-id:905380103;
        mso-list-template-ids:1334504036;}
@list l8:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l8:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9
        {mso-list-id:1057629996;
        mso-list-template-ids:116194294;}
@list l9:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l9:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10
        {mso-list-id:1171725683;
        mso-list-template-ids:440049248;}
@list l10:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l10:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11
        {mso-list-id:1293249411;
        mso-list-template-ids:-1906123760;}
@list l11:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l11:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l12
        {mso-list-id:1430394057;
        mso-list-template-ids:465094326;}
@list l12:level1
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level3
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level4
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level6
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level7
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l12:level9
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l13
        {mso-list-id:1479684475;
        mso-list-template-ids:187048720;}
@list l13:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l13:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l14
        {mso-list-id:1607151397;
        mso-list-template-ids:-1847681332;}
@list l14:level1
        {mso-level-start-at:7;
        mso-level-number-format:alpha-lower;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level3
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level4
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level6
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level7
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l14:level9
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l15
        {mso-list-id:1650011671;
        mso-list-template-ids:-1039253250;}
@list l15:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l15:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16
        {mso-list-id:2071220922;
        mso-list-template-ids:-857710108;}
@list l16:level1
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level2
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level3
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level4
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level5
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level6
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level7
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level8
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
@list l16:level9
        {mso-level-number-format:bullet;
        mso-level-text:;
        mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;
        mso-ansi-font-size:10.0pt;
        font-family:Symbol;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]--></head><body lang=EN-US link=blue vlink=purple style='word-wrap:break-word'><div class=WordSection1><p class=MsoNormal>Our goal is to make this widely repeatable without problems, used by anyone.  So we must get the simplest of cases sorted out first.<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>The pypi distribution of x3d.py does not behave the same as someone’s local copy of x3d.py package, presumably because of the __init__.py provided with it or possibly some other configuration file.  Sleuthing and testing continue.<o:p></o:p></p><div><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'><o:p> </o:p></span></p><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don<o:p></o:p></span></p><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'>-- <o:p></o:p></span></p><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        brutzman@nps.edu<o:p></o:p></span></p><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149<o:p></o:p></span></p><p class=MsoNormal><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'>faculty.nps.edu/brutzman<o:p></o:p></span></p></div><p class=MsoNormal><o:p> </o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal><b>From:</b> John Carlson <yottzumm@gmail.com> <br><b>Sent:</b> Monday, February 28, 2022 8:47 PM<br><b>To:</b> Brutzman, Donald (Don) (CIV) <brutzman@nps.edu><br><b>Cc:</b> Hans Moritz Guenther <hgunther@mit.edu>; Peitso, Loren (CIV) <lepeitso@nps.edu>; X3D Public Mailing List (x3d-public@web3d.org) <x3d-public@web3d.org><br><b>Subject:</b> Re: [x3d-public] x3d.py package: Some feedback and suggestion for improvement; tuples and lists; updated x3d.py<o:p></o:p></p></div></div><p class=MsoNormal><o:p> </o:p></p><div><div><p class=MsoNormal>Ok, Don.   I mostly just copy x3d.py to my source folder and set PYTHONPATH.  Maybe you could try in a conda or python virtual environment?<o:p></o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p></div><div><p class=MsoNormal><o:p> </o:p></p><div><div><p class=MsoNormal>On Mon, Feb 28, 2022 at 10:44 PM Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu">brutzman@nps.edu</a>> wrote:<o:p></o:p></p></div><blockquote style='border:none;border-left:solid #CCCCCC 1.0pt;padding:0in 0in 0in 6.0pt;margin-left:4.8pt;margin-right:0in'><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Have tried a few solutions to getting things working without having the double x3d.x3d import statement.  Unfortunately still not working.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Latest stable build remains<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l16 level1 lfo1'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpypi.org%2Fproject%2Fx3d%2F4.0.51&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068534923684%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=qAR3YuBR%2B%2BiaNpQDxJDuA5qXvnq9Qa%2BnTaq7NjyRpBs%3D&reserved=0" target="_blank">https://pypi.org/project/x3d/4.0.51</a><o:p></o:p></li></ul></div></div><div><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>-- </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a></span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'><a href="http://faculty.nps.edu/brutzman" target="_blank">faculty.nps.edu/brutzman</a></span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b>From:</b> Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>> <br><b>Sent:</b> Wednesday, February 23, 2022 7:30 AM<br><b>To:</b> X3D Public Mailing List (<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>) <<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>><br><b>Cc:</b> rlentz <<a href="mailto:rlentz@gmail.com" target="_blank">rlentz@gmail.com</a>>; Vince Marchetti <<a href="mailto:vmarchetti@kshell.com" target="_blank">vmarchetti@kshell.com</a>>; Mike McCann <<a href="mailto:mccann@mbari.org" target="_blank">mccann@mbari.org</a>>; Peitso, Loren (CIV) <<a href="mailto:lepeitso@nps.edu" target="_blank">lepeitso@nps.edu</a>>; Hans Moritz Guenther <<a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a>>; Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>><br><b>Subject:</b> RE: x3d.py package: Some feedback and suggestion for improvement; tuples and lists; updated x3d.py<o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Have released another build of x3d.py (aka X3DJSAIL), now version 4.0.51.<o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l4 level1 lfo2'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpypi.org%2Fproject%2Fx3d&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068534923684%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=GpgiuhJqxaudfdJtOYrnwIuPZ3CLQhIvwqDOIIs35Xg%3D&reserved=0" target="_blank">https://pypi.org/project/x3d</a><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l4 level1 lfo2'><a href="https://www.web3d.org/x3d/stylesheets/python/python.html" target="_blank">https://www.web3d.org/x3d/stylesheets/python/python.html</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Updated features include:<o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l11 level1 lfo3'>Override (and report) when a contained MetadataSet (or other Metadata*) node has incorrect containerField, providing extra care since default values changed in X3D4.  Similar checks are provided in X3dTidy stylesheet.<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l11 level1 lfo3'>When a conversion error to XML (.x3d output file) occurs, only print out 7 lines of result file containing the error (instead of the whole file).<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l11 level1 lfo3'>Proper handling of typed values contained by <field> or <fieldValue> initializations for Script nodes and prototypes.<o:p></o:p></li></ul><div style='border:none;border-bottom:double windowtext 2.25pt;padding:0in 0in 1.0pt 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>create python:<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>C:\x3d-code\<a href="http://www.web3d.org" target="_blank">www.web3d.org</a>\x3d\content\examples\X3dForWebAuthors/Chapter14Prototypes//TextStringPrototype.x3d processing with X3dToPython stylesheet...<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>C:\x3d-code\<a href="http://www.web3d.org" target="_blank">www.web3d.org</a>\x3d\content\examples\X3dForWebAuthors/Chapter14Prototypes//TextStringPrototype.py self-validation tests...<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>validate python:<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>x3d.py package loaded, have fun with X3D Graphics!<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Python-to-XML well-formed XML document  test of XML output complete<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Python-to-XML X3D 3.3 schema validation test of XML output complete<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Python-to-VRML export of VRML output successful<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Python-to-JSON export of JSON output successful (under development)<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>python x3d.py load and self-test complete for TextStringPrototype.py<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l1 level1 lfo4'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fx3dgraphics.com%2Fexamples%2FX3dForWebAuthors%2FChapter14Prototypes%2FTextStringPrototypeIndex.html&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=Uk348bX7tQojUW0lulEC2AxYpLq4mQ71ffykQ3L4nCU%3D&reserved=0" target="_blank">https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter14Prototypes/TextStringPrototypeIndex.html</a><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l1 level1 lfo4'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fx3dgraphics.com%2Fexamples%2FX3dForWebAuthors%2FChapter14Prototypes%2FTextStringPrototype.py&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=36nlPNQDihkvVqPfanqvhaA7hSncwLlTUb%2BFs%2BhVM%2FE%3D&reserved=0" target="_blank">https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter14Prototypes/TextStringPrototype.py</a> <o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l1 level1 lfo4'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fx3dgraphics.com%2Fexamples%2FX3dForWebAuthors%2FChapter14Prototypes%2FTextStringPrototype.x3d&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=kdMR%2BqL5SP5EaUHJ%2BSo6CUYCqw9u8EfPnzfdHiTqhYw%3D&reserved=0" target="_blank">https://x3dgraphics.com/examples/X3dForWebAuthors/Chapter14Prototypes/TextStringPrototype.x3d</a><o:p></o:p></li></ul><div style='border:none;border-bottom:double windowtext 2.25pt;padding:0in 0in 1.0pt 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>The regression tests are running very cleanly, when processing the X3D Examples archives a handful of errors in field initializations were detected and now few exceptions are reported.  Perhaps the only recurring exceptions are when some of our extremely large scans are encountered, too big for my laptop to published.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l10 level1 lfo5'><a href="https://www.web3d.org/x3d/content/examples/build.python.all.log.txt" target="_blank">https://www.web3d.org/x3d/content/examples/build.python.all.log.txt</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Also upgraded the Saxon xslt engine to version 11.2 of open-source home edition (HE), to good effect.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l0 level1 lfo6'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.saxonica.com%2Fhtml%2Fdocumentation11%2Fabout&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=rIZTPCbsndfbe9afWVseFHEvdxYOtE4JV8lN3XgtLAY%3D&reserved=0" target="_blank">https://www.saxonica.com/html/documentation11/about</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Have fun with X3D Python!   8)<o:p></o:p></p><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>-- </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a></span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'><a href="http://faculty.nps.edu/brutzman" target="_blank">faculty.nps.edu/brutzman</a></span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b>From:</b> Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>> <br><b>Sent:</b> Monday, February 21, 2022 9:41 AM<br><b>To:</b> Hans Moritz Guenther <<a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a>>; X3D Public Mailing List (<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>) <<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>><br><b>Cc:</b> rlentz <<a href="mailto:rlentz@gmail.com" target="_blank">rlentz@gmail.com</a>>; Vince Marchetti <<a href="mailto:vmarchetti@kshell.com" target="_blank">vmarchetti@kshell.com</a>>; Mike McCann <<a href="mailto:mccann@mbari.org" target="_blank">mccann@mbari.org</a>>; Peitso, Loren (CIV) <<a href="mailto:lepeitso@nps.edu" target="_blank">lepeitso@nps.edu</a>>; Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>><br><b>Subject:</b> RE: x3d.py package: Some feedback and suggestion for improvement; tuples and lists; updated x3d.py<o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Hello all python programmers.  Another x3d.py release is available.  Have performed a complete refactoring of field type setters as well as corresponding isValid* and assertValid* methods.  The field types now perform value validation whenever setting, and duck typing is preserved wherever possible up and down within the X3D field types.  Now published to PyPi with corresponding unit tests complete.<o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l5 level1 lfo7'>x3d 4.0.50 <o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l5 level1 lfo7'><span style='font-family:"Courier New"'>pip install x3d==4.0.50</span><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l5 level1 lfo7'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpypi.org%2Fproject%2Fx3d%2F4.0.50&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=NpG4f8AX2y3s83CBHDaoLLYIQ8Fo1%2F0a0kv4iO%2FKiKo%3D&reserved=0" target="_blank">https://pypi.org/project/x3d/4.0.50</a><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l5 level1 lfo7'>X3D Examples Archive regression tests, several post-test improvements are being added:<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l5 level1 lfo7'><a href="https://www.web3d.org/x3d/content/examples/build.python.all.log.txt" target="_blank">https://www.web3d.org/x3d/content/examples/build.python.all.log.txt</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Once again a large number of smoke tests have been added, confirming it is indeed a good way to check new functionality.<o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l6 level1 lfo8'><a href="https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py" target="_blank">https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py</a><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l6 level1 lfo8'><a href="https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt" target="_blank">https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>I’ve looked at numpy a little, but there are many types.  We’re ready to consider utility methods.  Up until now, I have hesitated to offer plain-array support for data types since that might easily break tuple checks.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Example(s) showing common use cases for X3D with numpy would be helpful, TIA.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>-- </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a></span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'><a href="http://faculty.nps.edu/brutzman" target="_blank">faculty.nps.edu/brutzman</a></span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b>From:</b> Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>> <br><b>Sent:</b> Sunday, February 13, 2022 11:08 PM<br><b>To:</b> Hans Moritz Guenther <<a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a>>; Peitso, Loren (CIV) <<a href="mailto:lepeitso@nps.edu" target="_blank">lepeitso@nps.edu</a>><br><b>Cc:</b> rlentz <<a href="mailto:rlentz@gmail.com" target="_blank">rlentz@gmail.com</a>>; Vince Marchetti <<a href="mailto:vmarchetti@kshell.com" target="_blank">vmarchetti@kshell.com</a>>; Mike McCann <<a href="mailto:mccann@mbari.org" target="_blank">mccann@mbari.org</a>>; X3D Public Mailing List (<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>) <<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>>; Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>><br><b>Subject:</b> RE: x3d.py package: Some feedback and suggestion for improvement; tuples and lists; updated x3d.py<o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>I have updated x3d.py to include  several points of progress.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ol start=1 type=a><li class=m6872490181044312902msolistparagraph style='mso-list:l12 level1 lfo9'>REGEX_PYTHON (perturbated from REGEX_XML) for all types.  Validation of each X3D-typed python value now includes regex checking.<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l12 level1 lfo9'>The standard python library ‘re’ for regexes is now imported by x3d.py<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l12 level1 lfo9'>In X3D XML Schemas and X3DUOM, made regex occurrence (\s) consistent throughout as \s<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l12 level1 lfo9'>Strings are now allowed as validatable values for some simple types.  More work needed for list and array combinations.<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l12 level1 lfo9'>Documentation and tests:<o:p></o:p></li></ol><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l2 level1 lfo10'>Python X3D Package x3d.py<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l2 level1 lfo10'>“The x3d.py Python X3D Package supports programmers with Python interfaces and objects for standards-based X3D programming, all as open source.”<o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l2 level1 lfo10'><a href="https://www.web3d.org/x3d/stylesheets/python/python.html" target="_blank">https://www.web3d.org/x3d/stylesheets/python/python.html</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ol start=6 type=a><li class=m6872490181044312902msolistparagraph style='mso-list:l7 level1 lfo11'>A large number of smoke tests have been added, this is indeed a good way to check new functionality.<o:p></o:p></li></ol><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l13 level1 lfo12'><a href="https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py" target="_blank">https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py</a><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l13 level1 lfo12'><a href="https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt" target="_blank">https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt</a><o:p></o:p></li></ul><p class=m6872490181044312902msolistparagraph style='margin-left:.25in'> <o:p></o:p></p><ol start=7 type=a><li class=m6872490181044312902msolistparagraph style='mso-list:l14 level1 lfo13'>Have added section on Data Types to X3D Python page.<o:p></o:p></li></ol><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l8 level1 lfo14'><a href="https://www.web3d.org/x3d/stylesheets/python/python.html#DataTypes" target="_blank">https://www.web3d.org/x3d/stylesheets/python/python.html#DataTypes</a><o:p></o:p></li></ul><div style='border:none;border-bottom:solid windowtext 1.5pt;padding:0in 0in 1.0pt 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b><span style='font-size:12.0pt'><a href="https://www.web3d.org/x3d/stylesheets/python/python.html#DataTypes" target="_blank"><span style='font-family:"Segoe UI Emoji",sans-serif'>🔖</span></a> Data Types </span></b><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>An important aspect of Python ease of programming is <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDuck_typing&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=FUFdStu0LT1I2NhU%2B1hSI6FdZWnqSG8PZqvu2FMH3%2FM%3D&reserved=0" target="_blank">duck typing</a>: If it looks like a duck and walks like a duck and quacks like a duck, then it must be a duck. <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.python.org%2F3%2Fglossary.html%23term-duck-typing&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=R8olvTeEu8%2BMV3%2BBaSOBzAc4prJW%2BpebDrtYJzKy3kc%3D&reserved=0" target="_blank">Python duck typing</a> describes how well-designed code improves its flexibility by allowing polymorphic substitution (i.e. <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FParametric_polymorphism&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=bEKubj5Q8el1I7IxCw3XevEZuKBWsp1yTsCiHpj4S7Y%3D&reserved=0" target="_blank">parametric polymorphism</a>). <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>X3D has strictly defined <a href="https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-CD1/Part01/fieldTypes.html" target="_blank" title="X3D Architecture specification, field types">field types</a> for every field of every node in the standard. This means that all values can be statically checked when defining a model, leading to high-quality model files. Further it is an error to provide an incorrectly typed value at run time, i.e. an SFString cannot be directly used as a numeric value. This approach precludes a wide range of errors that can occur in other languages. Details on each possible X3D type can be found at <a href="https://www.web3d.org/x3d/content/X3dTooltips.html#type" target="_blank">X3D Tooltips: types</a>. <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Strict typing checks are included when building X3D classes and assigning values to fields, for all X3D types. This strictness for input data prevents <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FGarbage_in%2C_garbage_out&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=%2Bl43ts1Zjx9OowkDsO3%2Fd%2BTzd4Nr7ZeV4QAim7e1QJo%3D&reserved=0" target="_blank">Garbage in, garbage out</a> pathologies when creating X3D models. <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><a href="https://www.web3d.org/specifications/X3dRegularExpressions.html" target="_blank">X3D regular expression (regex)</a> checking is applied to result values. Each type class includes string constants for <span style='font-size:10.0pt;font-family:"Courier New"'>REGEX_PYTHON</span> and <span style='font-size:10.0pt;font-family:"Courier New"'>REGEX_PYTHON</span> patterns. <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Python authors can confirm data validity and take further advantage of available type-checking methods, such as the following: <o:p></o:p></p><ul type=disc><li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l9 level1 lfo15'>Checks <i>value</i> expression and reports results as boolean: <br><span style='font-size:10.0pt;font-family:"Courier New"'>isValidSFBool(<i>value</i>)</span>, <span style='font-size:10.0pt;font-family:"Courier New"'>isValidSFVec3f(<i>value</i>)</span>, <span style='font-size:10.0pt;font-family:"Courier New"'>isValidMFRotation(<i>value</i>)</span>, etc. <o:p></o:p></li><li class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l9 level1 lfo15'>Checks <i>value</i> expression and assets valid results, or else Fails and throws traceback: <br><span style='font-size:10.0pt;font-family:"Courier New"'>assertValidSFBool(<i>value</i>)</span>, <span style='font-size:10.0pt;font-family:"Courier New"'>assertValidSFVec3f(<i>value</i>)</span>, <span style='font-size:10.0pt;font-family:"Courier New"'>assertValidMFRotation(<i>value</i>)</span>, etc. <o:p></o:p></li></ul><div style='border:none;border-bottom:solid windowtext 1.5pt;padding:0in 0in 1.0pt 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>The <span style='font-size:10.0pt;font-family:"Courier New"'>x3d.py python</span> package strives to support duck typing of unambiguous values while preserving strict X3D validation of all inputs. Example tests for each case can be found in <a href="https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py" target="_blank">examples/PythonX3dSmokeTests.py</a> with corresponding outputs shown in <a href="https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt" target="_blank">build.examples.log.txt</a><o:p></o:p></p></div><ol start=8 type=a><li class=m6872490181044312902msolistparagraph style='mso-list:l3 level1 lfo16'>Update available for installation/refresh<o:p></o:p></li></ol><ul type=disc><li class=m6872490181044312902msolistparagraph style='mso-list:l15 level1 lfo17'>x3d 4.0.49 <o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l15 level1 lfo17'><span style='font-family:"Courier New"'>pip install x3d==4.0.49</span><o:p></o:p></li><li class=m6872490181044312902msolistparagraph style='mso-list:l15 level1 lfo17'><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpypi.org%2Fproject%2Fx3d%2F4.0.49&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=YRWfx%2FJX6YtbB6l5SYkdTNFuuus9mSCytKSnmnVJzEw%3D&reserved=0" target="_blank">https://pypi.org/project/x3d/4.0.49</a><o:p></o:p></li></ul><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>g. It would be helpful to have a simple example program contributed that uses both x3d.py and numpy packages, this will doubtless inform current development future testing.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Unit tests appear to be passing, I hope these changes are indeed improvements.  Updated test reports welcome.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Have fun with X3D Python!  8)<o:p></o:p></p><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>-- </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a></span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'><a href="http://faculty.nps.edu/brutzman" target="_blank">faculty.nps.edu/brutzman</a></span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b>From:</b> Brutzman, Donald (Don) (CIV) <br><b>Sent:</b> Saturday, February 12, 2022 5:50 PM<br><b>To:</b> Hans Moritz Guenther <<a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a>>; Peitso, Loren (CIV) <<a href="mailto:lepeitso@nps.edu" target="_blank">lepeitso@nps.edu</a>><br><b>Cc:</b> rlentz <<a href="mailto:rlentz@gmail.com" target="_blank">rlentz@gmail.com</a>>; Vince Marchetti <<a href="mailto:vmarchetti@kshell.com" target="_blank">vmarchetti@kshell.com</a>>; Mike McCann <<a href="mailto:mccann@mbari.org" target="_blank">mccann@mbari.org</a>>; X3D Public Mailing List (<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>) <<a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a>>; <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a><br><b>Subject:</b> RE: x3d.py package: Some feedback and suggestion for improvement; tuples and lists<o:p></o:p></p></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>[Cc: x3d-public list, all design discussions are open and archived]<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Splitting out one issue at a time (see subject line please)<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div style='border:none;border-bottom:solid windowtext 1.5pt;padding:0in 0in 1.0pt 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>Moritz wrote:<o:p></o:p></p></div><div><p>This works: <o:p></o:p></p><p>x3d.Coordinate(point=[[0, 0, 0], (0, 1, 0), (1, 1, 0)])<o:p></o:p></p><p>This fails:<o:p></o:p></p><p>x3d.Coordinate(point=([0, 0, 0], (0, 1, 0), (1, 1, 0)))<o:p></o:p></p><pre><span class=m6872490181044312902ansi-red-fg>X3DTypeError</span>: ([0, 0, 0], (0, 1, 0), (1, 1, 0)), type=<class 'tuple'> is not a valid Python list for MFVec3f<o:p></o:p></pre><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><pre><span style='font-family:"Calibri",sans-serif'>Why not allow a tuple instead of a list here? The inner coordinates can be either tuples or lists.</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><div style='border:none;border-bottom:solid windowtext 1.5pt;padding:0in 0in 1.0pt 0in'><pre><span style='font-family:"Calibri",sans-serif'>I don't understand what that typechecking is good for here.</span><o:p></o:p></pre></div><pre> <o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>Background: X3D types include strongly typed tuples (Single Field Vector, SFVec* types) and ordered lists of those same types (Multiple Field Vector, MFVec* types)</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'>X3D4 Architecture, clause 5 Field type reference</span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'><a href="https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-CD1/Part01/fieldTypes.html" target="_blank">https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-CD1/Part01/fieldTypes.html</a></span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>further explained at</span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'>X3D Tooltips, Type definitions</span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'><a href="https://www.web3d.org/x3d/content/X3dTooltips.html#type" target="_blank">https://www.web3d.org/x3d/content/X3dTooltips.html#type</a></span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>Not yet described in the documentation, but evident in the code, is the design decision to use python tuples for SFVec types, and python lists for MFVec types.</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>This remains the most logical alignment with Python data structures.</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>Glad to see your test case provoked an understandable error!  Indeed </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>The intent of strictness is to avoid run-time problems that can easily arise from lackadaisical treatment of arrays.  Something like your example might look obvious in an example composed of numeric data, but might be much less clear if variables are used.</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>Am further concerned that if mixed combinations of values are allowed, then isolation of errors within long lists might not be possible/practical if error messages remain as terse as above (or, conversely, if error messages are immensely complex).</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>If is conceivable we might find some cases where we can safely add support.  For example, creating a new SFVec3f([1,2,3]) might be allowable.</span><o:p></o:p></pre><pre> <o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'>Am thinking we should be careful on this one.  Defining some simple acceptable variations that we might add to the Smoke Tests would be a good next step.</span><o:p></o:p></pre><pre><span style='font-family:"Calibri",sans-serif'> </span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'><a href="https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py" target="_blank">https://www.web3d.org/x3d/stylesheets/python/examples/PythonX3dSmokeTests.py</a></span><o:p></o:p></pre><pre style='margin-left:.5in'><span style='font-family:Symbol'>·</span><span style='font-size:7.0pt;font-family:"Times New Roman",serif'>        </span><span style='font-family:"Calibri",sans-serif'><a href="https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt" target="_blank">https://www.web3d.org/x3d/stylesheets/python/build.examples.log.txt</a></span><o:p></o:p></pre><pre> <o:p></o:p></pre><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt'>The place to put any relaxations will be in the type classes themselves, e.g. SFVec3f and MFVec3f etc.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>all the best, Don</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>-- </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Don Brutzman  Naval Postgraduate School, Code USW/Br        <a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a></span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>Watkins 270,  MOVES Institute, Monterey CA 93943-5000 USA    +1.831.656.2149</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:10.0pt;font-family:"Courier New"'>X3D graphics, virtual worlds, Navy robotics https://</span> <span style='font-size:10.0pt;font-family:"Courier New"'><a href="http://faculty.nps.edu/brutzman" target="_blank">faculty.nps.edu/brutzman</a></span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><div style='border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b>From:</b> Hans Moritz Guenther <<a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a>> <br><b>Sent:</b> Friday, February 11, 2022 7:45 PM<br><b>To:</b> Peitso, Loren (CIV) <<a href="mailto:lepeitso@nps.edu" target="_blank">lepeitso@nps.edu</a>><br><b>Cc:</b> Brutzman, Donald (Don) (CIV) <<a href="mailto:brutzman@nps.edu" target="_blank">brutzman@nps.edu</a>>; rlentz <<a href="mailto:rlentz@gmail.com" target="_blank">rlentz@gmail.com</a>>; Vince Marchetti <<a href="mailto:vmarchetti@kshell.com" target="_blank">vmarchetti@kshell.com</a>>; Mike McCann <<a href="mailto:mccann@mbari.org" target="_blank">mccann@mbari.org</a>><br><b>Subject:</b> Re: x3d.py package: Some feedback and suggestion for improvement<o:p></o:p></p></div></div><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>On 2/11/22 8:47 PM, Peitso, Loren (CIV) wrote:<o:p></o:p></p></div><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'>Hans,</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'>I have had a chat with Don and we are in full agreement that your described use case is important and would be quite useful for widening the usefulness of the x3d package.  Consideration for expanding usability is to not break anything (obviously), remain Pythonic, and not add additional code constructs/constraints that require consideration of architectural changes every time something needs adding or updating.   I'll propose a couple solutions by issue-raised and we can discuss debate from there.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p>" I need to use "x3d.x3d". That's of course possible but a little cumbersome."<o:p></o:p></p><p style='margin-left:.5in'>Agreed on cumbersome!  Have you tried   <span style='font-family:"Courier New"'>import x3d.x3d as x3d_  </span><span style='font-size:14.0pt'>?</span><o:p></o:p></p></blockquote><p>Sure (I even use import x3d.x3d as x3d). But it's not how most python packages work, stating with the standard library:<o:p></o:p></p><p>from os import chdir, not from os.os import chdir<o:p></o:p></p><p>from math import atanh, not from math.math import atanh<o:p></o:p></p><p>The same is true for common Python packages that are not part of the standard library:<o:p></o:p></p><p>import numpy as np; arr = np.ones(5), not import numpy.numpy as np<o:p></o:p></p><p>import pandas as pd, not import pandas.pandas as pd<o:p></o:p></p><p>It is of course your prerogative as package author to chose any naming scheme that seems sensible to you, but it is certainly unusual to import a package this way. It *does* make sense if you plan for more functionality in the future, e.g. if you think there could be other submodules such as<o:p></o:p></p><p>x3d.json<o:p></o:p></p><p>x3d.numpy_interface<o:p></o:p></p><p>x3d.utils<o:p></o:p></p><p>x3d.display<o:p></o:p></p><p>etc.<o:p></o:p></p><p>If that's the case, maybe a short comment in the code that explains the reasoning would be useful.<o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p style='mso-margin-top-alt:5.0pt;margin-right:0in;margin-bottom:12.0pt;margin-left:1.0in'> <o:p></o:p></p><p> <o:p></o:p></p><p>- the __init__.py file. The file is present, but not correct. It lists all the classes in __all__ but does not actually import them. Thus, that can't actually be used.<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:.5in'><span style='font-size:12.0pt'>Per <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.python.org%2F3%2Ftutorial%2Fmodules.html&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=aDwfgA6PN%2FlhFql191bM3P%2BqV%2FXyWfT5Fro4B5KSNNY%3D&reserved=0" target="_blank">https://docs.python.org/3/tutorial/modules.html</a>, section 6.4:  </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:12.0pt'>"In the simplest case,<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>__init__.py</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:12.0pt'> </span></span><span style='font-size:12.0pt'>can just be an empty file, but it can also execute initialization code for the package or set the<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>__all__</span></span><span style='font-size:12.0pt'>variable, described later."</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:12.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:.5in'><span style='font-size:12.0pt'>Per <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.python.org%2F3%2Ftutorial%2Fmodules.html&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=aDwfgA6PN%2FlhFql191bM3P%2BqV%2FXyWfT5Fro4B5KSNNY%3D&reserved=0" target="_blank">https://docs.python.org/3/tutorial/modules.html</a>, section 6.4.1:  </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:12.0pt'>"Although certain modules are designed to export only names that follow certain patterns when you use </span><span style='font-size:11.5pt'>import *</span><span style='font-size:12.0pt'>, it is still considered bad practice in production code."</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:12.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:.5in'><span style='font-size:12.0pt'>The "not actually import(ing) them part" is working as Python intends when we provide the empty __init__.py file (per 6.4.1)</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:12.0pt'>If<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>__all__</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:12.0pt'> </span></span><span style='font-size:12.0pt'>is not defined, the statement<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>from</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:11.5pt'> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>sound.effects</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:11.5pt'> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>import</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:11.5pt'> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>*</span></span><span style='font-size:12.0pt'>does<span class=m6872490181044312902apple-converted-space> </span><em><span style='font-family:"Calibri",sans-serif'>not</span></em><span class=m6872490181044312902apple-converted-space> </span>import all submodules from the package<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt;font-family:"Courier New ;color:#222222",serif'>sound.effects</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:12.0pt'> </span></span><span style='font-size:12.0pt'>into the current namespace; it only ensures that the package<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt;font-family:"Courier New ;color:#222222",serif'>sound.effects</span></span><span class=m6872490181044312902apple-converted-space><span style='font-size:12.0pt'> </span></span><span style='font-size:12.0pt'>has been imported (possibly running any initialization code in<span class=m6872490181044312902apple-converted-space> </span></span><span class=m6872490181044312902pre><span style='font-size:11.5pt'>__init__.py</span></span><span style='font-size:12.0pt'>) and then imports whatever names are defined in the package.<span class=m6872490181044312902apple-converted-space> </span></span><o:p></o:p></p><p style='margin-left:1.0in'><span style='font-size:14.0pt'>The __all__ attribute gets initialized during the importing, but only as far as described. Thus requiring the package name prepend for fully-qualified names to properly resolve.</span><o:p></o:p></p><p style='margin-left:.5in'><span style='font-size:14.0pt'>We have chosen the empty __init__.py file convention for simplicity, as there is no forced loading of any leading underscore names into the _all__ attribute and/or namespace by using this convention (respecting 6.4.1).  This aspect is a future-proofing against having to consider if future code changes need to consider __all__ or not.  With the empty __init__.py there are no potential forcing issues with respect to future code updates.</span><o:p></o:p></p></blockquote><p>Well, you don't import anything, then there is no reason to define __all__ in the __init__ file. In that case the file should just be empty. As the paragraphs that you pasted above explain, the purpose of __all__ is to limit what gets imported when I do "from x3d import *" for example, if the following is my __init__.py:<o:p></o:p></p><p>from math import sqrt<o:p></o:p></p><p>from <o:p></o:p></p><p>__all__ = ['answer', 'MyClass']<o:p></o:p></p><p>answer = 5<o:p></o:p></p><p>class MyClass():<o:p></o:p></p><p>    def calc(self, x):<o:p></o:p></p><p>        return sqrt(x)<o:p></o:p></p><p> <o:p></o:p></p><p> <o:p></o:p></p><p>then "from xxx import *" gives me "answer" and "MyClass". Without the __all__ I would get "answer" "MyClass" and <br>"sqrt" i.e. everything that's available inside __init__.py. Often, that's not what we want because sqrt is nothing specific from our package. So __all__ restricts that. In the version of x3d.py that I have the __init__ files lists a whole bunch of things in the __all__, but does not import them. So why is there an __all__ at all?<o:p></o:p></p><p>However, the most common case is to selectively import things into __init__, often including things like a version string (as __version__) and the most important classes or functions that user may want in the flat namespace.<o:p></o:p></p><p>Examples from pretty standard Python packages:<o:p></o:p></p><p><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Furllib3%2Furllib3%2Fblob%2Fmain%2Fsrc%2Furllib3%2F__init__.py%23L27&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=75qNENQXzrWbzfUcR2RfxeRBrYhndOppQR98oB2JSp8%3D&reserved=0" target="_blank">https://github.com/urllib3/urllib3/blob/main/src/urllib3/__init__.py#L27</a><o:p></o:p></p><p><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fpypa%2Fsetuptools%2Fblob%2Fmain%2Fsetuptools%2F__init__.py%23L24&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=S7ztgjAhw616K%2FQn08KucMBg4ScwqWxjilFQRL4Rlo4%3D&reserved=0" target="_blank">https://github.com/pypa/setuptools/blob/main/setuptools/__init__.py#L24</a><o:p></o:p></p><p><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnumpy%2Fnumpy%2Fblob%2Fmain%2Fnumpy%2F__init__.py&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=hhMiVoQZxAPIXx2ihuByHwFUC3Wan0C2q3alIy79i78%3D&reserved=0" target="_blank">https://github.com/numpy/numpy/blob/main/numpy/__init__.py</a><o:p></o:p></p><p> <o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'> <o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p style='margin-left:.5in'><span style='font-size:14.0pt'>Additionally, and as indicated by the bulk of the next two paragraphs, probably more importantly: I have been burned very badly in the past, entering Imports Hell, based on using an ill-timed from xyz import *   and had to explore the arcane and totally insane practice of putting import statements at the bottom of a file to partially attempt to compensate for import loading circular dependencies.  Putting </span><span style='font-family:"Courier New"'>from x3d import *</span>  <span style='font-size:14.0pt'>into the __init__.py file opens the door to that netherworld which awaits someone unluckily coding something that creates a circular dependency.  I can't guarantee that there is zero chance of that in coding up a solution for Numpy types, or other kinds of types that may be necessary in the future.  We would hate to inadvertently force a package user into that special hell.</span><o:p></o:p></p></blockquote><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'>I'm curious to learn how that works, since I've never seen a case where any other module imported anything from __init__.py Circular imports can happen and have happened to me, but never related to __init__, so I'm a little surprised. about that.<o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p><span style='font-size:14.0pt'> </span><o:p></o:p></p><p>- Python usually makes heavy use of duck-typing (you can use any object as long as it behaves as you expect) instead of explicit "isinstance" checks. A lot of numerical computations in Python are done using the numpy (<a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnumpy.org%2F&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=fkHhTb4EHuZvF%2FdeL6%2BnT628gnfGFpnmb8QNmZoyKIs%3D&reserved=0" target="_blank">https://numpy.org</a>) library, which implements array-based math and is optimized in C. Numpy has datatypes that are essentially similar to Python int but fail an isinstance checks, and I can pass those in some, but not all places in x3d.py. Here is an example that fails:<o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:.5in'><span style='font-size:14.0pt'>We agree that this use-case needs addressing to provide a Numpy compliant x3d.py experience.  Please provide some feedback on following proposal, design goals/constraints first.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:.5in'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'>1) Yes, in the implementation code there is a point where duck-typing must become enforced typing, because the x3d module is for implementing a specified standard which has specified types. </span><o:p></o:p></p></blockquote><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'>But the specific standard is not the Python type. As I understand it the standard says "integer" not "64 bit integer in big-endian". If I'm correct, any int will do (32 bin on a 32 bit machine, 64 bit on a 64 bit machine etc.). If that's case, then there is in fact no reason at all to case e.g. a numpy int64 to a Python int - on a 64-bit machine they are identical anyway. When I output the x3d to an xml file or a json file, they will all be converted to a string anyway. Then, it's even less important what type of int they where initially, because you won't even see the difference between an int64 and an int128.<o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'>So the type checking problem is unavoidable, it just comes down to at what layer of the code does a particular type run into an unavoidable requirement to finally do the type-check for a verified per-standard-construction.  The requirement has been addressed in code for the transition from basic types to the specified X3D types.  We know that so far this code works, so it would be nice to not rewrite all, or even any, of that working code.</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'>2) Users who are using native Python types as inputs should not feel any performance change when adding the necessary Numpy type use functionality to module x3d. Adding mandatory casts, or even conditionals for deciding casts, could add significant overhead if millions+ of values are processed so targeting only where the price must be paid no-matter-what is advisable.</span><o:p></o:p></p></blockquote><p>Performance is always and important consideration. However, it seems to me that this is premature optimization. We are not the first people thinking about this and it's not a bottleneck. On my machine, "if isinstance(a, int)" is about 1 ns faster than "int(a) = a" if a is an int already (that's the case of possible performance loss, if a is not an int already, then the current implementation fails anyway). <o:p></o:p></p><p>%timeit if isinstance(a, int): pass<o:p></o:p></p><pre>50.3 ns ± 0.266 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)<o:p></o:p></pre><pre>%timeit if int(a) == a: pass<o:p></o:p></pre><div><div><div><div><div><pre>51.4 ns ± 0.172 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)<o:p></o:p></pre></div></div></div></div></div><div><div><div><div><div><div><div><div><div><pre><span style='font-family:"Cambria Math",serif'>​</span><o:p></o:p></pre></div></div></div></div></div></div></div></div></div><p>That 1 ns is only 2% of the total runtime of the if statement and < 0.04 % of the total runtime of executing something useful where that statement matters. Here, I making the smallest IndexedFaceSet I can.  If that takes 1 ns longer, that's OK. If I can afford to run a 26.3 microsecond operation a few million times, I can afford to run a 26.301 microsecond operation - the difference is within the noise of the measurement.<o:p></o:p></p><p>%timeit out = x3d.IndexedFaceSet(coord=x3d.Coordinate(point=[(0, 0, 0), (0, 1, 0), (1, 1, 0)]), coordIndex=[0, 1, 2])<o:p></o:p></p><pre>26.3 µs ± 185 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)<o:p></o:p></pre><pre> <o:p></o:p></pre><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'>3) There must be some other communities out there, or in the future, that will have similar type issues specific to their use-case implementation, our Numpy solution should show the way forward for those, and in implementing those future updates require as little touching of verified-correct code as possible.</span><o:p></o:p></p></blockquote><p>Numpy by far the most important one. While counting the importance of libraries is a tricky business, in e.g. PEP 465 <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.python.org%2Fdev%2Fpeps%2Fpep-0465%2F%23but-isn-t-matrix-multiplication-a-pretty-niche-requirement&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=U2f98a1sr%2F32v9ks4mD3ewt7%2FS9USgwptltQkVBZbQs%3D&reserved=0" target="_blank">https://www.python.org/dev/peps/pep-0465/#but-isn-t-matrix-multiplication-a-pretty-niche-requirement</a> the authors found that numpy was the forth most used library - ranking far before most packages of even the standard library (only sys, os, and re and imported more often). That does not mean that there is not and won't ever be other ways to do numerics in Python ever, but it does mean the numpy is a pretty save bet.<o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><b><u><span style='font-size:14.0pt'>Proposal</span></u></b><span style='font-size:14.0pt'>:  Adding a submodule in the x3d module which serves as the interface layer between Numpy users and x3d.py.  Numpy users should only need to import their 'shim' from the x3d module  (As a non-binding name example:  </span><span style='font-family:"Courier New"'>import x3d.numpy2x3d as x3d_</span><span style='font-size:14.0pt'>  I don't think I like the specific name either, but we all get the idea)   and it will provide Numpy-compliant type input, do any necessary type casting on parameters, and then call working x3d.py code. It should have an equivalent call for every public API function/method in x3d.py, but only include the minimal necessary type-casting code to make the call.  Once developed, well tested and verified, the long term maintenance tail of this shim file should be mostly restricted to times when Numpy makes changes to its type system (VERY rare), and if the x3d.py API gets any public additions (bugs are bugs, and that's not the tail I'm addressing here as we have the duty to kill as many as possible early.)</span><o:p></o:p></p></blockquote><p>I think that will work - but it's not the most convenient for the user. I think the most convenient would be to iterate over any input, and don't check that the input is a list<o:p></o:p></p><p>This works: <o:p></o:p></p><p>x3d.Coordinate(point=[[0, 0, 0], (0, 1, 0), (1, 1, 0)])<o:p></o:p></p><p>This fails:<o:p></o:p></p><p>x3d.Coordinate(point=([0, 0, 0], (0, 1, 0), (1, 1, 0)))<o:p></o:p></p><pre><span class=m6872490181044312902ansi-red-fg>X3DTypeError</span>: ([0, 0, 0], (0, 1, 0), (1, 1, 0)), type=<class 'tuple'> is not a valid Python list for MFVec3f<o:p></o:p></pre><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><pre>Why not allow a tuple instead of a list here? The inner coordinates can be either tuples or lists.<o:p></o:p></pre><pre> <o:p></o:p></pre><pre>I don't understand what that typechecking is good for here.<o:p></o:p></pre><pre> <o:p></o:p></pre><pre>Moritz<o:p></o:p></pre><pre> <o:p></o:p></pre><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'> <o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:1.0in'><span style='font-size:14.0pt'>An added benefit of this flavor of solution is that your participation is not within the autogenerated x3d.py layer, so no concerns of breaking a working x3d.py file.  Writing this to be autogenerated as well is probably best practice, that way it significantly reduces eyeball-missed public API functions.  Don is the expert in the auto-generation aspect.  I helped him with x3d.py by describing the repeatable stylistic issues, conventions and some advantageous techniques to provide low-cost syntactic guarantees within chunks of code, he made the magic auto-happen, we debugged pairs style with him driving.  Setting up the Numpy-isms is likely a very similar flavor of project, but more favorably scoped as start-type and destination-type are both going to be defined the majority of the time, your expertise will be quite valuable in this process.</span><o:p></o:p></p></blockquote><p class=MsoNormal style='mso-margin-top-alt:auto;margin-bottom:12.0pt'>I have not started coding, but I would be surprised if more than 100 line of code are needed for this. <o:p></o:p></p><blockquote style='margin-top:5.0pt;margin-bottom:5.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'>Looking forward to seeing your thoughts.  Have a great weekend!</span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><div><div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'>v/r  Loren <o:p></o:p></p></div></div></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><span style='font-size:14.0pt'> </span><o:p></o:p></p><div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'><b><span style='font-size:12.0pt;color:black'>From: </span></b><span style='font-size:12.0pt;color:black'>Hans Moritz Guenther <a href="mailto:hgunther@mit.edu" target="_blank"><hgunther@mit.edu></a><br><b>Date: </b>Monday, February 7, 2022 at 7:06 AM<br><b>To: </b>Don Brutzman <a href="mailto:brutzman@nps.edu" target="_blank"><brutzman@nps.edu></a>, "Peitso, Loren (CIV)" <a href="mailto:lepeitso@nps.edu" target="_blank"><lepeitso@nps.edu></a><br><b>Subject: </b>x3d.py package: Some feedback and suggestion for improvement</span><o:p></o:p></p></div><div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p></div><div style='border:solid #004679 1.0pt;padding:2.0pt 2.0pt 2.0pt 2.0pt'><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;line-height:12.0pt;background:#004679'><span style='font-size:10.0pt;color:yellow'>NPS WARNING: *external sender* verify before acting.</span><o:p></o:p></p></div><p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto'> <o:p></o:p></p><div><p>Hi,<o:p></o:p></p><p>I'm starting to use the x3d.py library to generate x3d output. <o:p></o:p></p><p>For a little bit of background, I'm an astronomer and one of the things I do is ray-tracing of the designs for new instruments. It is then very useful to have some form of output that allows me to put images of how the instrument looks and what path the photons take into presentations or on the web. As it turns out, X3D is a great format for that, see e.g. <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fspace.mit.edu%2Fhome%2Fguenther%2FARCUS%2F3Dview.html&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=HswE1JMvLifJ%2BDP4bCzFszjaexvzgBNbxY3p5Y8E2KY%3D&reserved=0" target="_blank">https://space.mit.edu/home/guenther/ARCUS/3Dview.html</a><o:p></o:p></p><p>In that past, I've generated the X3D output through a python package called mayavi (<a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.enthought.com%2Fmayavi%2Fmayavi%2F&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=Mvmxs7gYHPRI3PDazHkRBLpdrlyZnH9WADV6hBaYOls%3D&reserved=0" target="_blank">https://docs.enthought.com/mayavi/mayavi/</a>) but that is a rather heavy dependency for this very limited purpose. So, I'm trying to convert to use your x3d.py library instead.<o:p></o:p></p><p>I'm very much a Python programmer using what people call the "scientific stack" in Python (the libraries numpy, scipy, pandas, etc.) with very little experience in 3D visualization or web-programming. First, let me thank you for your effort to make x3d.py at all. In general it works great!<o:p></o:p></p><p>I'd like to mention a few glitches I've run into so far, and since I did not see in your package description what's the preferred way to give feedback I decided to just email you. Please let me know if you prefer me to raise those in a different forum (mailing list, sourceforge ticket ...).<o:p></o:p></p><p>I might be able to help with some of this, but since you say your package is autogenerated, there is little use for me to change the code I have; presumably all changes need to be done to the code that you use to autogenerate things.<o:p></o:p></p><p>OK, here are a few things I noticed:<o:p></o:p></p><p>- the __init__.py file. The file is present, but not correct. It lists all the classes in __all__ but does not actually import them. Thus, that can't actually be used.<o:p></o:p></p><p>I<span style='font-family:"Courier New"'>n [1]: import x3d<br><br>In [2]: x3d.IndexedTriangleSet<br>---------------------------------------------------------------------------<br>AttributeError                            Traceback (most recent call last)<br><ipython-input-2-93a98d5f0414> in <module><br>----> 1 x3d.IndexedTriangleSet</span><o:p></o:p></p><p>Instead, I need to use "x3d.x3d". That's of course possible but a little cumbersome.<o:p></o:p></p><p><span style='font-family:"Courier New"'>In [4]: import x3d.x3d<br>x3d.py package loaded, have fun with X3D Graphics!<br><br>In [5]: x3d.x3d.IndexedTriangleSet<br>Out[5]: x3d.x3d.IndexedTriangleSet</span><o:p></o:p></p><p>I think all that's missing is the following line in the __init__.py<o:p></o:p></p><p><span style='font-family:"Courier New"'>from x3d import *</span><o:p></o:p></p><p>which will import everything that's defined in x3d.py into the __init__.py namespace and than the user an get it from "import x3d" which gives you the names defined in x3d/__init__.py<o:p></o:p></p><p>- IndexTriangleSet: Coordinates are given as a list of tuples [(x1, y1, z1), (x2, y2, y3), ...]. I think that makes sense. It orders stuff in a natural way and is easy to understand. However, the index needs to be given as a flat list [ind11, ind12, ind13, ind21, ind22, ind23, ...]. Since there are three indices per triangle, I think the same list of tuples as for the coordinates would make more sense [( ind11, ind12, ind13), (ind21, ind22, ind23) ...]<o:p></o:p></p><p>- Python usually makes heavy use of duck-typing (you can use any object as long as it behaves as you expect) instead of explicit "isinstance" checks. A lot of numerical computations in Python are done using the numpy (<a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnumpy.org%2F&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=fkHhTb4EHuZvF%2FdeL6%2BnT628gnfGFpnmb8QNmZoyKIs%3D&reserved=0" target="_blank">https://numpy.org</a>) library, which implements array-based math and is optimized in C. Numpy has datatypes that are essentially similar to Python int but fail an isinstance checks, and I can pass those in some, but not all places in x3d.py. Here is an example that fails:<o:p></o:p></p><pre> <o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>~/mambaforge/envs/kitchensink/lib/python3.10/site-packages/x3d/x3d.py</span> in <span class=m6872490181044312902ansi-cyan-fg>__init__</span><span class=m6872490181044312902ansi-blue-fg>(self, ccw, colorPerVertex, index, normalPerVertex, solid, color, coord, fogCoord, normal, texCoord, attrib, DEF, USE, IS, metadata, class_, id_, style_)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48191</span>         self<span class=m6872490181044312902ansi-blue-fg>.</span>ccw <span class=m6872490181044312902ansi-blue-fg>=</span> ccw<o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48192</span>         self<span class=m6872490181044312902ansi-blue-fg>.</span>colorPerVertex <span class=m6872490181044312902ansi-blue-fg>=</span> colorPerVertex<o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>> 48193</span><span class=m6872490181044312902ansi-red-fg>         </span>self<span class=m6872490181044312902ansi-blue-fg>.</span>index <span class=m6872490181044312902ansi-blue-fg>=</span> index<o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48194</span>         self<span class=m6872490181044312902ansi-blue-fg>.</span>normalPerVertex <span class=m6872490181044312902ansi-blue-fg>=</span> normalPerVertex<o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48195</span>         self<span class=m6872490181044312902ansi-blue-fg>.</span>solid <span class=m6872490181044312902ansi-blue-fg>=</span> solid<o:p></o:p></pre><pre> <o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>~/mambaforge/envs/kitchensink/lib/python3.10/site-packages/x3d/x3d.py</span> in <span class=m6872490181044312902ansi-cyan-fg>index</span><span class=m6872490181044312902ansi-blue-fg>(self, index)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48230</span>         <span class=m6872490181044312902ansi-green-fg>if</span>  index <span class=m6872490181044312902ansi-green-fg>is</span> <span class=m6872490181044312902ansi-green-fg>None</span><span class=m6872490181044312902ansi-blue-fg>:</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48231</span>             index <span class=m6872490181044312902ansi-blue-fg>=</span> MFInt32<span class=m6872490181044312902ansi-blue-fg>.</span>DEFAULT_VALUE<span class=m6872490181044312902ansi-blue-fg>()</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>> 48232</span><span class=m6872490181044312902ansi-red-fg>         </span>assertValidMFInt32<span class=m6872490181044312902ansi-blue-fg>(</span>index<span class=m6872490181044312902ansi-blue-fg>)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48233</span>         assertNonNegative<span class=m6872490181044312902ansi-blue-fg>('index',</span> index<span class=m6872490181044312902ansi-blue-fg>)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>  48234</span>         self<span class=m6872490181044312902ansi-blue-fg>.</span>__index <span class=m6872490181044312902ansi-blue-fg>=</span> index<o:p></o:p></pre><pre> <o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>~/mambaforge/envs/kitchensink/lib/python3.10/site-packages/x3d/x3d.py</span> in <span class=m6872490181044312902ansi-cyan-fg>assertValidMFInt32</span><span class=m6872490181044312902ansi-blue-fg>(value)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>   2622</span>         <span class=m6872490181044312902ansi-green-fg>if</span> <span class=m6872490181044312902ansi-green-fg>not</span> isinstance<span class=m6872490181044312902ansi-blue-fg>(</span>each<span class=m6872490181044312902ansi-blue-fg>,</span> int<span class=m6872490181044312902ansi-blue-fg>):</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>   2623</span>             <span class=m6872490181044312902ansi-red-fg># print(flush=True)</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-fg>-> 2624</span><span class=m6872490181044312902ansi-red-fg>             </span><span class=m6872490181044312902ansi-green-fg>raise</span> X3DTypeError<span class=m6872490181044312902ansi-blue-fg>('MFInt32 list has contained value='</span> <span class=m6872490181044312902ansi-blue-fg>+</span> str<span class=m6872490181044312902ansi-blue-fg>(</span>each<span class=m6872490181044312902ansi-blue-fg>)</span> <span class=m6872490181044312902ansi-blue-fg>+</span> <span class=m6872490181044312902ansi-blue-fg>' with type='</span> <span class=m6872490181044312902ansi-blue-fg>+</span> str<span class=m6872490181044312902ansi-blue-fg>(</span>type<span class=m6872490181044312902ansi-blue-fg>(</span>each<span class=m6872490181044312902ansi-blue-fg>))</span> <span class=m6872490181044312902ansi-blue-fg>+</span> <span class=m6872490181044312902ansi-blue-fg>' which is not a valid int')</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>   2625</span>     <span class=m6872490181044312902ansi-green-fg>if</span> <span class=m6872490181044312902ansi-green-fg>not</span> isValidMFInt32<span class=m6872490181044312902ansi-blue-fg>(</span>value<span class=m6872490181044312902ansi-blue-fg>):</span><o:p></o:p></pre><pre><span class=m6872490181044312902ansi-green-intense-fg>   2626</span>         <span class=m6872490181044312902ansi-red-fg># print(flush=True)</span><o:p></o:p></pre><pre> <o:p></o:p></pre><pre><span class=m6872490181044312902ansi-red-fg>X3DTypeError</span>: MFInt32 list has contained value=1 with type=<class 'numpy.int64'> which is not a valid int<o:p></o:p></pre><pre> <o:p></o:p></pre><pre> <o:p></o:p></pre><pre>There is an easy workaround:<o:p></o:p></pre><pre> <o:p></o:p></pre><pre>x3d.IndexedTriangleSet(index = [int(x) for x in myindex]<o:p></o:p></pre><pre> <o:p></o:p></pre><pre>but it would be nice if IndexedTriangleSet accepted my numbers directly. <o:p></o:p></pre><pre>There are several ways of doing that, but the easiest is probably to change line 2622 to:<o:p></o:p></pre><pre> <o:p></o:p></pre><pre>if not int(x) == x: <o:p></o:p></pre><pre> <o:p></o:p></pre><p>That will work for any object that can be converted to an int, including numpy, python decimal, fraction, ..<o:p></o:p></p><p>- Numpy: One might consider taking numpy arrays directly, i.e. instead of <o:p></o:p></p><p>x3d.Coordinate(point=[(x1, y1, z1), (x2, y2, y3), ...])<o:p></o:p></p><p>one could do <o:p></o:p></p><p>x3d.Coordinate(point=arr)<o:p></o:p></p><p>where arr is a (3, n) numpy array. Now, if done naively that would require numpy as a dependency to x3d.py and it's probably good to avoid that. However, there are ways to accept numpy arrays without requiring numpy. That's a little more involved, but can be done (for example using decorators or a separate module (x3d.numpy_interface) or separate package (x3d-numpy)). Not sure if it's worth the effort at this point - that depends on what your future plans for this package are and how fast this is developing.<o:p></o:p></p><p>- Changelog. From the pypi entry and the docs on <a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.web3d.org%2Fx3d%2Fstylesheets%2Fpython%2Fpython.html&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=t%2Ffv6kKz4LsLFFF%2F0xyjBOBM5lMCq0Q1F0OMlndLXNM%3D&reserved=0" target="_blank">https://www.web3d.org/x3d/stylesheets/python/python.html</a> it was not quite clear to me how stable the package is or where I would see changes listed, for example, if you do the change that I suggested above (accepting index values as tuples instead of a flat list) that would break the interface. Would you do that? Where would I find a list of changes from one version to another?<o:p></o:p></p><p>- Jupyter notebook: I'm working on a wrapper to automatically display any x3d representation in the jupyter notebook. It's only a few lines of code, and I'm happy to share (it could be integrated into the Python package x3d easily), but I want to use it a little longer for myself to make sure I have the kinks worked out so I don't pass code with major bugs to you.<o:p></o:p></p><p>Please let me know if there is anything I can do to help with this awesome package, that really makes generating X3D from Python so much simpler already.<o:p></o:p></p><p>Yours,<o:p></o:p></p><p>Moritz<o:p></o:p></p><p> <o:p></o:p></p><pre>-- <o:p></o:p></pre><pre>Hans Moritz Günther<o:p></o:p></pre><pre>Massachusetts Institute of Technology<o:p></o:p></pre><pre>Kavli Institute for Astrophysics and Space Research<o:p></o:p></pre><pre><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.google.com%2Fmaps%2Fsearch%2F77%2BMassachusetts%2BAvenue%3Fentry%3Dgmail%26source%3Dg&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=gipBFfR48z%2FCBtZfputL7KIJ4zxGIrOCZP46YA9z%2Bzk%3D&reserved=0">77 Massachusetts Avenue</a><o:p></o:p></pre><pre>NE83-569<o:p></o:p></pre><pre>Cambridge, MA 02139<o:p></o:p></pre><pre><a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a><o:p></o:p></pre><pre><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fspace.mit.edu%2Fhome%2Fguenther%2F&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=ptllE4gfc9quq8hbcgd0uX5cODE8xsdmM7oJ%2BYvM5cY%3D&reserved=0" target="_blank">https://space.mit.edu/home/guenther/</a><o:p></o:p></pre></div></blockquote><pre>-- <o:p></o:p></pre><pre>Hans Moritz Günther<o:p></o:p></pre><pre>Massachusetts Institute of Technology<o:p></o:p></pre><pre>Kavli Institute for Astrophysics and Space Research<o:p></o:p></pre><pre><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.google.com%2Fmaps%2Fsearch%2F77%2BMassachusetts%2BAvenue%3Fentry%3Dgmail%26source%3Dg&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=gipBFfR48z%2FCBtZfputL7KIJ4zxGIrOCZP46YA9z%2Bzk%3D&reserved=0">77 Massachusetts Avenue</a><o:p></o:p></pre><pre>NE83-569<o:p></o:p></pre><pre>Cambridge, MA 02139<o:p></o:p></pre><pre><a href="mailto:hgunther@mit.edu" target="_blank">hgunther@mit.edu</a><o:p></o:p></pre><pre><a href="https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fspace.mit.edu%2Fhome%2Fguenther%2F&data=04%7C01%7Cbrutzman%40nps.edu%7Cd52f5f0e06e44fdd0d7508d9fb3e95e7%7C6d936231a51740ea9199f7578963378e%7C0%7C0%7C637817068535079964%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0&sdata=ptllE4gfc9quq8hbcgd0uX5cODE8xsdmM7oJ%2BYvM5cY%3D&reserved=0" target="_blank">https://space.mit.edu/home/guenther/</a><o:p></o:p></pre></div></div></div><p class=MsoNormal>_______________________________________________<br>x3d-public mailing list<br><a href="mailto:x3d-public@web3d.org" target="_blank">x3d-public@web3d.org</a><br><a href="http://web3d.org/mailman/listinfo/x3d-public_web3d.org" target="_blank">http://web3d.org/mailman/listinfo/x3d-public_web3d.org</a><o:p></o:p></p></blockquote></div></div></div></div></body></html>