anchor.diff:  ~a
   and
   ~a" u-a u-b))))
  +
  +;;-------------------------------------------------------------------------
  +
  +
  +;; TODO
  +;;
  +;; - sv! taking vectors
  +;; - rename to vec-space
  +
  +;;-------------------------------------------------------------------------
  +
  +(deferror from-ndc-or-screen-cpu-side () ()
  +    "Cepl.Spaces: Limitations in cpu-side space transforms from
  +                   *screen-space* & *ndc-space*
  +
  +CEPL's `spaces` feature can transform between most spaces in the graph, however
  +there are limitations for *screen-space* & *ndc-space*
  +
  +Transforming from *screen-space* or *ndc-space* back to *clip-space* (or earlier)
  +is not expressible as a matrix. Instead it is a function using data only[0]
  +available in the fragment shader (the gl-frag-coord).
  +
  +CEPL therefore requires you to explicitly specify the variable *screen-space*
  +or *ndc-space* in your shader code and does not allow you to pass in either as a
  +uniform[1].
  +
  +By having this limitation we guarentee we can give you the most performance we
  +can for these cases.
  +
  +Notes:
  +[0] technically we could return a function that you then called with the
  +    screen-space vec4, however this would mean you couldnt be certain if you
  +    were going to recieve a matrix or a function requiring more conditionals
  +    and incurring a performance hit.
  +    CEPL prefers to optimize for the normal case and clarify the details of the
  +    edge cases in the documentation (and this error message)
  +
  +[1] One possible way of avoiding this issue would be to upload a struct that
  +    contained a matrix or a flag specifying that a [ndc/screen]-space->space function
  +    should be used. However this would require lots of 'if' statements inside your
  +    glsl code, which is a great way to destroy the performace of your code.")
  +
  +(deferror to-ndc-or-screen () ()
  +    "Cepl.Spaces: Un-implemented Transform Feature
  +
  +Sorry we do not currently support transforming to *ndc-space* or *screen-space*
  +This is a gap in the api and will be looked at in future versions.")
  +
  +(deferror from-ndc () ()
  +    "Cepl.Spaces: Un-implemented Transform Feature
  +
  +Sorry we do not currently support transforming from *ndc-space*
  +This is a gap in the api and will be looked at in future versions.")
  +
  +;; (defun-g screen-space-to-clip-space ((ss-pos :vec4) (viewport :vec4))
  +;;   (/ (v! (- (* (v:s~ ss-pos :xy) 2.0)
  +;; 	    (/ (* (v:s~ viewport :xy) 2.0)
  +;; 	       (* (v:s~ viewport :zw) 2.0))
  +;; 	    (v! 1 1))
  +;; 	 (/ (- (* 2.0 (v:z gl-frag-coord))
  +;; 	       (near gl-depth-range)
  +;; 	       (far gl-depth-range))
  +;; 	    (- (near gl-depth-range) (far gl-depth-range)))
  +;; 	 1.0)
  +;;      (v:w gl-frag-coord)))
  
positive.code:   (declare (ignore space body))
    (error "the 'in' macro can only be used inside shaders"))
  
  (v-defspecial space-boundary-convert ((form-obj v-type))
    :return
    (if (v-typep (code-type form-obj) 'v-svec)
        (let* ((outer-space (get-symbol-binding '*current-space* nil env)))
          (if (variable-in-scope-p '*current-space* env)
              (let* ((inner-name (get-space-from-svec form-obj env))
                     (outer-name (get-current-space env))
                     (implicit-uniform-name
                      (symb inner-name :-to- outer-name :-mat4))
                     (injected (inject-implicit-uniform
                                implicit-uniform-name :mat4 env
                                `(get-transform ,inner-name ,outer-name))))
                (vbind (obj env) (compile-form `(svec-* ,form-obj ,injected) env)
                  (add-space-to-meta (flow-ids obj) (v-type outer-space) env)
                  (let ((ast (node-tree obj)))
                    (values
                     (copy-code
                      obj
                      :node-tree (ast-node! 'space-boundary-convert
                                            (ast-args ast)
                                            (ast-return-type ast)
                                            (ast-starting-env ast)
                                            (ast-ending-env ast)))
                     env))))
              (values
               (let ((ast (node-tree form-obj)))
                 (copy-code
                  form-obj
                  :node-tree (ast-node! 'space-boundary-convert
                                        (list ast)
                                        (ast-return-type ast)
                                        (ast-starting-env ast)
                                        (ast-ending-env ast))))
               env)))
        (values form-obj env)))
  
  ;; this func isn't in the ast, which means the build won't
  ;; stabilize. Add a copy-code and replace the svec-* ast-node
  (v-defun svec-* (a b) "(~a * ~a)" (v-mat4 v-svec) 1)
  (v-defun svec-* (a b) "(~a * ~a)" (v-svec v-mat4) 0)
  
  (defmethod combine-metadata ((meta-a space-meta)
                               (meta-b space-meta))
  
negative.code:                                             (current-line w))
                        :to-block (mapcat #'to-block args)
                        :node-tree (ast-node!
                                    'v!
                                    (mapcar #'node-tree args)
                                    type
                                    env
  