x3dom / x3dom

X3DOM. A framework for integrating and manipulating X3D scenes as HTML5/DOM elements.
http://x3dom.org
Other
815 stars 271 forks source link

nodeField changed by protoField will not trigger ROUTE #1177

Closed microaaron closed 2 years ago

microaaron commented 2 years ago

Here is the example: https://jsitor.com/MBwavo1Vg

<X3D profile="Immersive" version="3.2">
  <Scene>    
    <ProtoDeclare name='testbox'>
      <ProtoInterface>
        <field name='translation' type='SFVec3f' accessType='inputOutput'></field>  
      </ProtoInterface>
      <ProtoBody> 
        <Group>
          <Transform DEF="box1">
            <IS>
              <connect nodeField="translation" protoField="translation"></connect>
            </IS>  
            <Shape>
              <Box size="1 1 1"></Box>
              <Appearance>
                <Material diffuseColor="0 1 0"></Material>
              </Appearance>
            </Shape>
          </Transform>
          <Transform translation="2.5 0 0"> 
            <Transform DEF="box2"> 
              <Shape>
                <Box size="1 1 1"></Box>
                <Appearance>
                  <Material diffuseColor="0 0 1"></Material>
                </Appearance>
              </Shape>
            </Transform>
          </Transform>
          <ROUTE fromNode="box1" fromField="translation" toNode="box2" toField="translation"></ROUTE> <!--doesn't work-->
        </Group>
    <Group>             
    </Group>
      </ProtoBody>
    </ProtoDeclare>
  </Scene>
</X3D>
andreasplesch commented 2 years ago

Yes, I think that should work. Can you find a similar example test in https://github.com/x3dom/x3dom/tree/master/test/functional/protos ?

andreasplesch commented 2 years ago

https://jsitor.com/FPSiL8lF9 has a potential fix. Since proto handling is quite involved, it will be necessary to test against all proto examples. An additional fieldWatcher for the proto wrapper node now forwards events in to IS nodes. Since there is also a similar forwarder from inside the Proto out to the proto wrapper, there is a loop. So that outForwarder is disabled when the inForwarder is invoked. That was the most challenging to determine.

Could you please test with your setup ? Thanks.

microaaron commented 2 years ago

Thanks, It works now. But what about routing from ProtoInstance like this: https://jsitor.com/zJwD5eNRs Died loop problem really makes things complex.

HTML:

<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge" />
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://x3dom.org/download/1.8.2/x3dom-full.debug.js"></script>
    <link rel='stylesheet' type='text/css' href='https://www.x3dom.org/release/x3dom.css'>
    </link>
</head>

<body>
    <X3D showLog="true" profile="Immersive" version="3.2">
        <Scene id="rootScene">
            <ExternProtoDeclare name="testbox"
                url="https://raw.githubusercontent.com/microaaron/ProtoExample/main/routeInProtoBody2.x3d">
            </ExternProtoDeclare>
            <ProtoInstance name="testbox" DEF="testbox">
                <fieldValue name="translation" value="0 0 0" />
            </ProtoInstance>
            <Transform translation="2.5 0 0">
                <Transform DEF="box2">
                    <Shape>
                        <Box size="1 1 1"></Box>
                        <Appearance>
                            <Material diffuseColor="0 0 1"></Material>
                        </Appearance>
                    </Shape>
                </Transform>
            </Transform>
            <ROUTE fromNode="testbox" fromField="translatione_changed" toNode="box2" toField="set_translation"></ROUTE> <!--routing from ProtoInstance doesn't work-->
        </Scene>
    </X3D>
</body>

</html>

routeInProtoBody2.x3d:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.2//EN" "http://www.web3d.org/specifications/x3d-3.2.dtd">

<X3D profile="Immersive" version="3.2">
  <Scene>    
    <ProtoDeclare name='testbox'>
      <ProtoInterface>
        <field name='translation' type='SFVec3f' accessType='inputOutput'></field>  
      </ProtoInterface>
      <ProtoBody> 
        <Group>
          <Transform DEF="box1">
            <IS>
              <connect nodeField="translation" protoField="translation"></connect>
            </IS>  
            <Shape>
              <Box size="1 1 1"></Box>
              <Appearance>
                <Material diffuseColor="0 1 0"></Material>
              </Appearance>
            </Shape>
          </Transform>
          <TimeSensor DEF="Timer" cycleInterval="5" loop="true"></TimeSensor>
          <PositionInterpolator DEF="PI" key="0 0.25 0.5 0.75 1" keyValue="0 0 0, 0.5 0 0, 0 0 0, -0.5 0 0, 0 0 0">
          </PositionInterpolator>
          <ROUTE fromNode="Timer" fromField="fraction_changed" toNode="PI" toField="set_fraction"></ROUTE>
          <ROUTE fromNode="PI" fromField="value_changed" toNode="box1" toField="set_translation"></ROUTE>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
  </Scene>
</X3D>
andreasplesch commented 2 years ago

I think there was a typo: 'translatione'

https://jsitor.com/NQevIMewX

---on the phone---

On Wed, Nov 24, 2021, 1:37 AM microaaron @.***> wrote:

Thanks, It works now. But what about routing from ProtoInstance like this: https://jsitor.com/zJwD5eNRs Died loop problem really make things complex.

HTML:

routeInProtoBody2.x3d:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.2//EN" "http://www.web3d.org/specifications/x3d-3.2.dtd">

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/x3dom/x3dom/issues/1177#issuecomment-977575764, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPCT22UOZPSX3LSMXD7IWTUNSB3VANCNFSM5ITI7BCA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

microaaron commented 2 years ago

It works. Thanks very much.

andreasplesch commented 2 years ago

Here the revised method for ProtoDeclaration

      _setupFieldWatchers: function (field) {
          this.declaration._protoBody._ISRoutes[field].forEach(function (ISNode) {
            var instanceNode = this.innerNameSpace.defMap[ISNode.nodeDEF];
            if (instanceNode == undefined) {
              var ISparent = this.protoBodyClone.querySelector("[DEF=" + ISNode.nodeDEF + "]");
              if (ISparent.tagName.toLowerCase() == "protoinstance") {
                if (this._externTries++ < this._maxTries) {
                  x3dom.debug.logWarning(" retrying ExternProto: " + this._externTries);
                  //try again
                  var timer = setTimeout(this._setupFieldWatchers.bind(this), 1000, field);
                }
              }
              return;
            }

            var nodeField = this._normalizeName(ISNode.nodeField, instanceNode);
            if (!instanceNode._fieldWatchers[nodeField]) {
              instanceNode._fieldWatchers[nodeField] = [];
            }
            var outForwarder = this.postMessage.bind(this, field);
            var _watchers = instanceNode._fieldWatchers[nodeField];
            _watchers.push(outForwarder); // forward out
            // remember outForwardex index for below
            var outForwarder_index = _watchers.length - 1;

            // AP: added event in forwarding from proto node to instance node so routes can pick it up
            if (!this._fieldWatchers[field]) {
              this._fieldWatchers[field] = [];
            }
            var inForwarder = (function (msg)
            // avoid cycle
            { // remove above fieldwatcher
              _watchers.splice(outForwarder_index, 1);
              instanceNode.postMessage(nodeField, msg);
              // add back above fieldwatcher
              _watchers.push(outForwarder);
              outForwarder_index = _watchers.length - 1; // update
            }).bind(instanceNode);
            this._fieldWatchers[field].push(inForwarder); // forward in
            //instanceNode.postMessage.bind( instanceNode, nodeField ) ); // forward in
          }, this);
        },