vendredi 23 février 2018

Threejs: How could we import a loader into a React application?

Hello and thank you for reading this question. 😉

I am trying to achive a use case where I need to load a NRRD file into a canvas in a React application.

I have studied Javascript during some months, and react intensive for weeks, and I am being introduced in Threejs.

I have been studying how to integrate React and Three, using this example as a reference:

How to connect Threejs to React?

After being able to load that example as a new react appliaction, I would like to use one of the Threejs' loaders to load the NRRD image.

I have tryed the following:

Our index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import * as THREE from "three";

class Scene extends React.Component {
    constructor(props) {
        super(props)

        this.start = this.start.bind(this)
        this.stop = this.stop.bind(this)
        this.animate = this.animate.bind(this)
    }

    componentDidMount() {
        const width = this.mount.clientWidth
        const height = this.mount.clientHeight

        const scene = new THREE.Scene()
        const camera = new THREE.PerspectiveCamera(
            75,
            width / height,
            0.1,
            1000
        )

        var loader = new THREE.NRRDLoader();
        loader.load("models/columnasegmentado01.nrrd", function (volume) {
            var sliceZ;


            //z plane

            var indexZ = 0;
            sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));
            scene.add(sliceZ.mesh);

        });

        const renderer = new THREE.WebGLRenderer({antialias: true})


        camera.position.z = 4
        renderer.setClearColor('#000000')
        renderer.setSize(width, height)

        this.scene = scene
        this.camera = camera
        this.renderer = renderer

        this.mount.appendChild(this.renderer.domElement)
        this.start()
    }

    componentWillUnmount() {
        this.stop()
        this.mount.removeChild(this.renderer.domElement)
    }

    start() {
        if (!this.frameId) {
            this.frameId = requestAnimationFrame(this.animate)
        }
    }

    stop() {
        cancelAnimationFrame(this.frameId)
    }

    animate() {


        this.renderScene()
        this.frameId = window.requestAnimationFrame(this.animate)
    }

    renderScene() {
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div
                style=
                ref={(mount) => {
                    this.mount = mount
                }}
            />
        )
    }
}

ReactDOM.render(<Scene/>, document.getElementById('root'))

And in the index.html we have the div being refered by id as root.

The question here is: how do we solve the:

Failed to compile
./src/index.js
  Line 26:  'NRRDLoader' is not defined  no-undef

Search for the keywords to learn more about each error.

I ask because I think it should be a straightforward question to solve, however I am not able to figure it out.

For further details, our NRRDLoader is in the node_modules/three/examples/js/loaders subdirectory.

My first try was to load the file, copying it into the rect-app/src/js directory and load it with a script tag in index.html as:

<script src="../src/js/NRRDLoader.js" }></script>

And referencing it from the index.js as:

/*global THREE.NRRDLoader*/


        var loader = new THREE.NRRDLoader();

However, there is a conflict, because THREE namespace is being imported from three npm module so then the IDE does not apply the global. For that reason we do have an Unresolved type: NRRDLoader

As an image:

enter image description here

I decided to give a try to that approach because of previously I have adked how to load an external script into react, that time was from a CDN, and I got that one working with globals:

REACT: How could we use an external library as a script, into a class or functional component? Is it possible?

Well, after reading more information in this topic: How to add external javascript file with reactjs

I decided to try the require approach:

So then in index.js we add:

require('./js/NRRDLoader');

And the result in the web browser is:

Failed to compile
./src/js/NRRDLoader.js
  Line 1:    'THREE' is not defined  no-undef
  Line 3:    'THREE' is not defined  no-undef
  Line 8:    'THREE' is not defined  no-undef
  Line 10:   'THREE' is not defined  no-undef
  Line 16:   'THREE' is not defined  no-undef
  Line 154:  'THREE' is not defined  no-undef
  Line 181:  'THREE' is not defined  no-undef
  Line 181:  'THREE' is not defined  no-undef
  Line 181:  'THREE' is not defined  no-undef
  Line 312:  'THREE' is not defined  no-undef
  Line 332:  'THREE' is not defined  no-undef
  Line 334:  'THREE' is not defined  no-undef
  Line 336:  'THREE' is not defined  no-undef
  Line 342:  'THREE' is not defined  no-undef
  Line 380:  'THREE' is not defined  no-undef
  Line 382:  'THREE' is not defined  no-undef

Well, at least it recognized the NRRDLoader. So then we should import Three in that file too:

Being the code as result:

import * as THREE from "three";

THREE.NRRDLoader = function (manager) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;


};

THREE.NRRDLoader.prototype = {

... more code ...

And it looks like we have a similar imports/exports difficult in NRRDLoader itself:

    Failed to compile
    ./src/js/NRRDLoader.js
    10:21-37 "export 'NRRDLoader' (imported as 'THREE') was not found in 'three'

My next naive step was to try to change the namespace and delete THREE from THREE.NRRDLoader declaration in our local NRRDloader file:

import * as THREE from "three";

NRRDLoader = function (manager) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;


};

NRRDLoader.prototype = {

... more code ...

And our web favorite browser reports:

Failed to compile
./src/js/NRRDLoader.js
  Line 3:   'NRRDLoader' is not defined  no-undef
  Line 10:  'NRRDLoader' is not defined  no-undef
  Line 12:  'NRRDLoader' is not defined  no-undef

Search for the keywords to learn more about each error.

So I think the key question is: how do we include or require external js files which doest not have export utility in our own project? 😉

In addition to explain why I am trying to integrate react and three, the purpose is to use the helpful way react offers us to structure UI components, combined whith the tools given by three as the load which is required in this use case.

As a note I have correctly tested and verified that a Threejs pure HTML/Javscript app loads correctly an NRRD image from local here is the github repo:

https://github.com/YoneMoreno/LoadNRRDInThreeJSExample

Could you help me plase figuring out how to solve this issue? Thank you

For further details about the code I am showing in this post where we would like to include/reqjuire/import the NRRDLoader from ThreeJS see the repo where I saved the tryed: https://github.com/YoneMoreno/IntegrateReactThreePlainCanvas




Aucun commentaire:

Enregistrer un commentaire