genworks / gendl

Archive of old version, visit gendl.org for current version --- #gendl on libera.chat
http://www.gendl.org
GNU Affero General Public License v3.0
57 stars 5 forks source link

Selecting geometry in X3D #138

Open tobievandenberg opened 11 years ago

tobievandenberg commented 11 years ago

The anchors are not working in X3D, so geometry cannot be selected in that mode and associated onclick functions cannot be used.

It seems to work fine in VRML.

The issue can be seen by viewing robot:assembly in X3D mode.

genworks commented 11 years ago

Thank you for the report. We want to stabilize anchors for use in VRML, X3D as well as X3DOM.

Can you indicate what OS, browser, and VRML/X3D plugin you are using?

tobievandenberg commented 11 years ago

Hi Dave,

It was on windows 7, with firefox using the BS Contact plugin.

Best, Tobie

From: Dave Cooper [mailto:notifications@github.com] Sent: vrijdag 6 september 2013 16:13 To: genworks/gendl Cc: Tobie van den Berg - LR Subject: Re: [gendl] Selecting geometry in X3D (#138)

Thank you for the report. We want to stabilize anchors for use in VRML, X3D as well as X3DOM.

Can you indicate what OS, browser, and VRML/X3D plugin you are using?

— Reply to this email directly or view it on GitHubhttps://github.com/genworks/gendl/issues/138#issuecomment-23942516.

genworks commented 9 years ago

Open for submission: $200-300

Audit and clean up selecting and onclick behavior for X3D and X3DOM output-formats (in theory, these should be identical).

reiniervandijk commented 9 years ago

The fix below to X3D lens adds onclick behavior to X3DOM output. Nice! It required some cleanup of code and an extra parameters attribute. However, there seems to be a strange bug in X3D mode. Whenever the javascript string to eval contains whitespace or comma's, a javascript error is thrown.

Uncaught SyntaxError: Unexpected end of input

At this stage I can't get my head around this. Perhaps a BSContact-related thingy? Strange, because the VRML counterpart is quite identical, and does work.

reiniervandijk commented 9 years ago

The fix for now:

(define-lens (x3d base-object)()
  :output-functions
  (
(cad-output
    (&key (header? t))

    (when header? (write-the header))

    (let ((orientation (the orientation*)))

      (let ((rotation (quaternion-to-rotation (matrix-to-quaternion orientation)))
            ;;(rotation (matrix-to-rotation orientation))
            (x (get-x (the center)))
            (y (get-y (the center))) 
            (z (get-z (the center)))
            )

        (cl-who:with-html-output (*stream* nil :indent nil)
      ((:|Transform| :|translation| (unless (every #'zerop (list x y z))
                      (format nil "~3,7f ~3,7f ~3,7f" x y z)))
       ((:|Transform| :|rotation| (when rotation
                    (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                        (get-x rotation) (get-y rotation) (get-z rotation) (get-w rotation))))

        (cond

          ((or (getf (the display-controls) :billboard)
           (getf (the display-controls) :billboard-vector))
           (cl-who:htm
        ((:|Billboard| :|axisOfRotation|  
           (if (getf (the display-controls) :billboard-vector)
               (let ((vector (getf (the display-controls) :billboard-vector)))
             (format nil "~a ~a ~a" (get-x vector) (get-y vector) (get-z vector)))
               (format nil "0 0 0")))
         (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
             (cl-who:htm
              ((:|Anchor| :|url| (format nil "javascript:event=null;~a" (funcall *onclick-function* self)) ;;event=null;
             :|parameter| "target=_self")
               (write-the shape)))
             (write-the shape)))))

          (t 
           (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
           (cl-who:htm
            ((:|Anchor| :|url| (format nil "javascript:event=null;~a" (funcall *onclick-function* self)) ;;event=null;
               :|parameter| "target=_self")
             (write-the shape)))
           (write-the shape)))))

       (when (and (typep self 'outline-specialization-mixin)
              (not (ignore-errors (typep self (read-from-string "surf:surface"))))
              (not (ignore-errors (typep self (read-from-string "surf:curve")))))
         (let* ((center (reverse-vector (the center)))
            (x (get-x center)) (y (get-y center)) (z (get-z center))
            (inverse (when (the orientation*)
                   (matrix-to-rotation (matrix:transpose-matrix 
                            (the orientation*)))
                   #+nil
                   (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix 
                                          (the orientation*)))))))
           (cl-who:htm

        ((:|Transform| :|rotation| (when inverse
                         (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                             (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))
         ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                         (format nil "~3,7f ~3,7f ~3,7f" x y z)))

          (mapc #'(lambda(outline-object)
                (write-the-object outline-object (cad-output :header? nil)))
            (the outline-leaves))

          ))

        )))

       #+nil
       (when (and (typep self 'outline-specialization-mixin)
              (not (ignore-errors (typep self (read-from-string "surf:surface"))))
              (not (ignore-errors (typep self (read-from-string "surf:curve")))))
         (let* ((center (reverse-vector (the center)))
            (x (get-x center)) (y (get-y center)) (z (get-z center))
            (inverse (when (the orientation*)
                   (matrix-to-rotation (matrix:transpose-matrix 
                            (the orientation*)))
                   #+nil
                   (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix 
                                          (the orientation*)))))))
           (cl-who:htm

        ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                        (format nil "~3,7f, ~3,7f, ~3,7f" x y z)))
         ((:|Transform| :|rotation| (when inverse
                          (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                              (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))

          (mapc #'(lambda(outline-object)
                (write-the-object outline-object (cad-output :header? nil)))
            (the outline-leaves))

          ))

        ))))))))

(cad-output-tree
    (&key (header? t) (from-root? t))

    (when header? (write-the header))

    (let ((center (if from-root? (the center) (the local-center*)))
      (orientation (if from-root? (the orientation*) (the local-orientation*))))
      (let ((rotation (quaternion-to-rotation (matrix-to-quaternion orientation)))
            ;;(rotation (matrix-to-rotation orientation))
            (x (get-x center)) (y (get-y center)) (z (get-z center)))

    ;;
    ;; FLAG -- factor out the redundant calls and call directly to
    ;; cad-output, which will itself call to the shape.
    ;;
        (cl-who:with-html-output (*stream* nil :indent nil)
          ((:|Transform| :|translation| (unless (every #'zerop (list x y z))
                      (format nil "~3,7f ~3,7f ~3,7f" x y z)))
       ((:|Transform| :|rotation| (when rotation
                    (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                        (get-x rotation) (get-y rotation) (get-z rotation) (get-w rotation)))

          )

        (if (null (the children))

        (cond

          ((or (getf (the display-controls) :billboard)
               (getf (the display-controls) :billboard-vector))
           (cl-who:htm
            ((:|Billboard| :|axisOfRotation|  
               (if (getf (the display-controls) :billboard-vector)
               (let ((vector (getf (the display-controls) :billboard-vector)))
                 (format nil "~a ~a ~a" (get-x vector) (get-y vector) (get-z vector)))
               (format nil "0 0 0")))

             (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
               (cl-who:htm
            ((:|Anchor| :|url| (format nil "javascript:event=null;~a" (funcall *onclick-function* self)) ;;event=null;
               :|parameter| "target=_self")
             (write-the shape)))
               (write-the shape)))))

          (t
           (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
               (cl-who:htm
            ((:|Anchor| :|url| (format nil "javascript:event=null;~a" (funcall *onclick-function* self)) ;;event=null;
               :|parameter| "target=_self")
             (write-the shape)))
               (write-the shape))))

        (mapc #'(lambda(child) 
              (write-the-object child (cad-output-tree :header? nil :from-root? nil))) (the children)))

        (when (and (typep self 'outline-specialization-mixin)
               (not (ignore-errors (typep self (read-from-string "surf:surface"))))
               (not (ignore-errors (typep self (read-from-string "surf:curve")))))

          (let* ((center (reverse-vector (the center)))
             (x (get-x center)) (y (get-y center)) (z (get-z center))
             (inverse (when (the orientation*)
                (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix (the orientation*))))
                #+nil
                (matrix-to-rotation (matrix:transpose-matrix (the orientation*))))))
        (cl-who:htm

         ((:|Transform| :|rotation| (when inverse
                          (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                              (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))

          ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                          (format nil "~3,7f ~3,7f ~3,7f" x y z)))

           (mapc #'(lambda(outline-object)
                 (write-the-object outline-object (cad-output :header? nil)))
             (the outline-leaves))
           ))
         )))

        #+nil
        (when (and (typep self 'outline-specialization-mixin)
               (not (ignore-errors (typep self (read-from-string "surf:surface"))))
               (not (ignore-errors (typep self (read-from-string "surf:curve")))))

          (let* ((center (reverse-vector (the center)))
             (x (get-x center)) (y (get-y center)) (z (get-z center))
             (inverse (when (the orientation*)
                (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix (the orientation*))))
                #+nil
                (matrix-to-rotation (matrix:transpose-matrix (the orientation*))))))
        (cl-who:htm

         ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                         (format nil "~3,7f, ~3,7f, ~3,7f" x y z)))
          ((:|Transform| :|rotation| (when inverse
                           (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                               (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))

           (mapc #'(lambda(outline-object)
                 (write-the-object outline-object (cad-output :header? nil)))
             (the outline-leaves))
           ))
         )))))))))

))
genworks commented 9 years ago

Is that apparent Javascript error only affecting X3D in BS Contact, or X3DOM as well?

We received the following support response from BS Contact a while back:

thanks for using BS Contact. The Anchor url call is not proper MFString encoded. Please replace the url string with the following string:

'"javascript:alert(\"Hey,Now\");"'

I hope this solves your question.

This was in response to an example we provided which looked like:

javascript:alert('Hey, Now');"

which had been throwing errors. I'm not sure if this is connected to the bug you are seeing or not, putting it out here just in case.

By the way, are we sure that Is the way to add anchors in X3DOM is in fact the same as X3D?

In X3DOM isn't it possible just to use a standard HTML element and add events to it, for example

<span onclick=gdlAjax(...)> ... some x3d content... </span>

reiniervandijk commented 9 years ago

For X3DOM the proposed fix above works like a charm. Use of proper MFStrings does indeed fix X3D format. See the fix below. However, now X3DOM fails, it complain about a syntax error. While browsing through the DOM tree, I now see " "code" " instead of ' "code" ', hmmm. Concerning your previous remark about onclick: I know this, but it works only for X3DOM-with-browser and not for X3D-with-BSContact (correct me if I'm wrong), what about making a separate lens for X3DOM then? thereby largely re-using X3D code. Note that both fixes collectively propose solutions to X3D and X3DOM, but I need input from you to continue.

reiniervandijk commented 9 years ago

The fix for X3D:

(in-package :geom-base)

(defun %gdlajax-MFString% (str)
  "string. Converts gdlajax string to MFString format for X3D output. Example:
  gdl-user> (setq str \"gdlajax(null, 'args=XXXX')\")
  gdl-user> (%gdlajax-MFString% str)
  \"'\"javascript:event=null;gdlAjax(null,\\\\\"args=DQooOmlpZCAiMTMxZDg3YjE3MDAiIDpiYXNoZWUgKDolcnAlIG5pbCkgOmZ1bmN0aW9uIDpwZXJmb3JtLWFjdGlvbiEgOmFyZ3VtZW50cw0KICgoOiVycCUgKDpmb290IDpiYXNlIDpyb2JvdCA6cm9vdC1vYmplY3Qtb2JqZWN0KSkpKQ\\\\\",true);\"'\""
  (concatenate 'string "'\"javascript:event=null;" (ppcre:regex-replace-all "'" (remove #\Space str) "\\\"") "\"'"))

(define-lens (x3d base-object)()
  :output-functions
  (
(cad-output
    (&key (header? t))

    (when header? (write-the header))

    (let ((orientation (the orientation*)))

      (let ((rotation (quaternion-to-rotation (matrix-to-quaternion orientation)))
            ;;(rotation (matrix-to-rotation orientation))
            (x (get-x (the center)))
            (y (get-y (the center))) 
            (z (get-z (the center)))
            )

        (cl-who:with-html-output (*stream* nil :indent nil)
      ((:|Transform| :|translation| (unless (every #'zerop (list x y z))
                      (format nil "~3,7f ~3,7f ~3,7f" x y z)))
       ((:|Transform| :|rotation| (when rotation
                    (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                        (get-x rotation) (get-y rotation) (get-z rotation) (get-w rotation))))

        (cond

          ((or (getf (the display-controls) :billboard)
           (getf (the display-controls) :billboard-vector))
           (cl-who:htm
        ((:|Billboard| :|axisOfRotation|  
           (if (getf (the display-controls) :billboard-vector)
               (let ((vector (getf (the display-controls) :billboard-vector)))
             (format nil "~a ~a ~a" (get-x vector) (get-y vector) (get-z vector)))
               (format nil "0 0 0")))
         (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
             (cl-who:htm
              ((:|Anchor|
             ;; use cl-who:fmt to force ' ' around string and not standard " "
             ;; strange: without :|url| nothing will be printed, so leave it.
             :|url| (cl-who:fmt " url=~a" (%gdlajax-MFString% (funcall *onclick-function* self)))
             :|parameter| "target=_self")
               (write-the shape)))
             (write-the shape)))))

          (t 
           (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
           (cl-who:htm
            ((:|Anchor|
               ;; use cl-who:fmt to force ' ' around string and not standard " "
               ;; strange: without :|url| nothing will be printed, so leave it.
               :|url| (cl-who:fmt " url=~a" (%gdlajax-MFString% (funcall *onclick-function* self)))
               :|parameter| "target=_self")
             (write-the shape)))
           (write-the shape)))))

       (when (and (typep self 'outline-specialization-mixin)
              (not (ignore-errors (typep self (read-from-string "surf:surface"))))
              (not (ignore-errors (typep self (read-from-string "surf:curve")))))
         (let* ((center (reverse-vector (the center)))
            (x (get-x center)) (y (get-y center)) (z (get-z center))
            (inverse (when (the orientation*)
                   (matrix-to-rotation (matrix:transpose-matrix 
                            (the orientation*)))
                   #+nil
                   (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix 
                                          (the orientation*)))))))
           (cl-who:htm

        ((:|Transform| :|rotation| (when inverse
                         (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                             (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))
         ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                         (format nil "~3,7f ~3,7f ~3,7f" x y z)))

          (mapc #'(lambda(outline-object)
                (write-the-object outline-object (cad-output :header? nil)))
            (the outline-leaves))

          ))

        )))

       )))))

(cad-output-tree
    (&key (header? t) (from-root? t))

    (when header? (write-the header))

    (let ((center (if from-root? (the center) (the local-center*)))
      (orientation (if from-root? (the orientation*) (the local-orientation*))))
      (let ((rotation (quaternion-to-rotation (matrix-to-quaternion orientation)))
            ;;(rotation (matrix-to-rotation orientation))
            (x (get-x center)) (y (get-y center)) (z (get-z center)))

    ;;
    ;; FLAG -- factor out the redundant calls and call directly to
    ;; cad-output, which will itself call to the shape.
    ;;
        (cl-who:with-html-output (*stream* nil :indent nil)
          ((:|Transform| :|translation| (unless (every #'zerop (list x y z))
                      (format nil "~3,7f ~3,7f ~3,7f" x y z)))
       ((:|Transform| :|rotation| (when rotation
                    (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                        (get-x rotation) (get-y rotation) (get-z rotation) (get-w rotation)))

          )

        (if (null (the children))

        (cond

          ((or (getf (the display-controls) :billboard)
               (getf (the display-controls) :billboard-vector))
           (cl-who:htm
            ((:|Billboard| :|axisOfRotation|  
               (if (getf (the display-controls) :billboard-vector)
               (let ((vector (getf (the display-controls) :billboard-vector)))
                 (format nil "~a ~a ~a" (get-x vector) (get-y vector) (get-z vector)))
               (format nil "0 0 0")))

             (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
             (cl-who:htm
              ((:|Anchor|
                 ;; use cl-who:fmt to force ' ' around string and not standard " "
                 ;; strange: without :|url| nothing will be printed, so leave it.
                 :|url| (cl-who:fmt " url=~a" (%gdlajax-MFString% (funcall *onclick-function* self)))
                 :|parameter| "target=_self")
               (write-the shape)))
             (write-the shape)))))

          (t
           (if (and (find-package :gwl) (symbol-value (read-safe-string "gwl::*onclick-function*")))
               (cl-who:htm
            ((:|Anchor|
               ;; use cl-who:fmt to force ' ' around string and not standard " "
               ;; strange: without :|url| nothing will be printed, so leave it.
               :|url| (cl-who:fmt " url=~a" (%gdlajax-MFString% (funcall *onclick-function* self)))
               :|parameter| "target=_self")
             (write-the shape)))
               (write-the shape))))

        (mapc #'(lambda(child) 
              (write-the-object child (cad-output-tree :header? nil :from-root? nil))) (the children)))

        (when (and (typep self 'outline-specialization-mixin)
               (not (ignore-errors (typep self (read-from-string "surf:surface"))))
               (not (ignore-errors (typep self (read-from-string "surf:curve")))))

          (let* ((center (reverse-vector (the center)))
             (x (get-x center)) (y (get-y center)) (z (get-z center))
             (inverse (when (the orientation*)
                (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix (the orientation*))))
                #+nil
                (matrix-to-rotation (matrix:transpose-matrix (the orientation*))))))
        (cl-who:htm

         ((:|Transform| :|rotation| (when inverse
                          (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                              (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))

          ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                          (format nil "~3,7f ~3,7f ~3,7f" x y z)))

           (mapc #'(lambda(outline-object)
                 (write-the-object outline-object (cad-output :header? nil)))
             (the outline-leaves))
           ))
         )))

        #+nil
        (when (and (typep self 'outline-specialization-mixin)
               (not (ignore-errors (typep self (read-from-string "surf:surface"))))
               (not (ignore-errors (typep self (read-from-string "surf:curve")))))

          (let* ((center (reverse-vector (the center)))
             (x (get-x center)) (y (get-y center)) (z (get-z center))
             (inverse (when (the orientation*)
                (quaternion-to-rotation (matrix-to-quaternion (matrix:transpose-matrix (the orientation*))))
                #+nil
                (matrix-to-rotation (matrix:transpose-matrix (the orientation*))))))
        (cl-who:htm

         ((:|Transform| :|translation| (when (not (every #'zerop (list x y z)))
                         (format nil "~3,7f, ~3,7f, ~3,7f" x y z)))
          ((:|Transform| :|rotation| (when inverse
                           (format nil "~3,7f ~3,7f ~3,7f ~3,7f"
                               (get-x inverse) (get-y inverse) (get-z inverse) (get-w inverse))))

           (mapc #'(lambda(outline-object)
                 (write-the-object outline-object (cad-output :header? nil)))
             (the outline-leaves))
           ))
         )))))))))
))
genworks commented 9 years ago

Yes, since x3dom and x3d (as benchmarked by BS Contact) seem to diverge on handling Anchors, let's have the lenses emit two different "flavors" of x3d -- one tuned for BS Contact and one tuned for x3dom.

Of course this should be done with minimal duplication of code - just a format-slot should do it, i.e.:

(with-format (x3d stream :flavor :x3dom) ... )

or

(with-format (x3d stream :flavor :x3d-plugin) ...)

I would lean toward the x3dom version using a standard :onclick DOM node, when the intention is to run a function.

On the other hand, what about the ability to have a graphical element be an actual anchor, in the traditional sense --- that is, pointing to an actual URL? Then we would want something like:

((:a :href ... ) ... )

so, do we need both an onclick-function and an anchor? And I guess without the possibility of having both at the same time...