简体   繁体   中英

What's the difference between object in scalajs scope and *same* object in js.global scope?

I'm trying to write a simple example to render cube with THREEJS lib.

package three

import org.scalajs.dom

import scala.scalajs.js
import scala.scalajs.js.Dynamic._
import scala.scalajs.js.annotation.JSName

object THREE extends js.Object
{
  @JSName ("THREE.Scene")
  class Scene extends js.Object
  {
       def add(obj: js.Object) = ???
  }

  @JSName ("THREE.Vector3")
  class Vector3(var x:js.Number, var y:js.Number, var z:js.Number) extends js.Object

  @JSName ("THREE.PerspectiveCamera")
  class PerspectiveCamera(a:js.Number,b:js.Number,c:js.Number,d:js.Number) extends js.Object
  {
    var position:Vector3 = _
  }

  @JSName ("THREE.WebGLRenderer")
  class WebGLRenderer(params:js.Dynamic) extends js.Object
  {
    def render(scene:js.Object, camera:js.Object) = ???
  }

  @JSName ("THREE.WebGLRenderer")
  class SimpleWebGLRenderer() extends js.Object
  {
    def render(scene:js.Object, camera:js.Object) = ???
    var domElement : js.Dynamic = _
  }

  @JSName ("THREE.BoxGeometry")
  class BoxGeometry(a:js.Number,b:js.Number,c:js.Number) extends js.Object

  @JSName ("THREE.MeshBasicMaterial")
  class MeshBasicMaterial(params:js.Dynamic) extends js.Object

  @JSName ("THREE.MeshBasicMaterial")
  class SimpleMeshBasicMaterial() extends js.Object

  @JSName ("THREE.Mesh")
  class Mesh(geometry:js.Object, material:js.Object) extends js.Object

}


object ThreeExample
{
  def render() =
  {
    val scene = new THREE.Scene()
    val camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
    val renderer = new THREE.WebGLRenderer(js.Dynamic.literal(
      canvas = global.document.getElementById("ThreeCanvas")
    ));
    val geometry = new THREE.BoxGeometry(1,1,1);
    val material = new THREE.SimpleMeshBasicMaterial()
    camera.position.z = 2;
    val cube = new THREE.Mesh(geometry, material);

Now the fun part. If I try to write

scene.add(cube);
renderer.render(scene, camera);

I get

ScalaJS.c.jl_ClassCastException {s$1: "[object Object] is not an instance of scala.runtime.Nothing$", e$1: null, stackTrace$1: null, stackdata: TypeError, constructor: function…}

error. While if I try

global._scene = scene
global._cube = cube
global._camera = camera
global._renderer = renderer

global._scene.add(global._cube)
global._renderer.render(global._scene, global._camera);

everything renders correctly w/o errors. I mean, what's the catch? Is there a difference between global._scene and scene or global._scene.add and scene.add ?

To elaborate on my comment: there is basically no difference. The statically typed interface is only a typed facade to the same mechanisms that are used with the dynamically typed interface. However, because it is static, it adds checks to the values returned by the methods you call.

In your case, when calling renderer.render() , the compiler adds a check that the value returned by this JavaScript method actually conforms to the static result type of WebGLRenderer.render() . But what is this result type? It's Nothing ! Why is that? Because the body of this method is ??? , which is of type Nothing , so scalac infers the method's result type as Nothing . The check then fails, because the value returned by render from JavaScript is not of type Nothing (obviously, since no value has that type), which causes the ClassCastException .

What you wanted is obviously not Nothing , but rather Unit . So all you have to do is to make explicit this result type with : Unit like this:

def render(scene:js.Object, camera:js.Object): Unit = ???

In general, you should always specify explicitly the result type of methods in facade types, because otherwise they're inferred as Nothing , which is not what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM