0

I am having troubles with accessing properties even though they exist in the object, but typescript tells me they do not. I create a particles object, which contains the geometry of the shape, and a material. I need to be able to access the geometry vertices of the particle, but typescript throws a fit when I try. For example, this.particles.geometry.vertices.length should return the length of my vertices, but typescript says vertices do not exist in geometry when it is actually there.

My problem occurs after the image is loaded, vertices are created, and then I need to move them to there destination index, but it says the this.particles.geometry.vertices does not exist.

Here is my particle object which contains the geometry, and inside the geometry is a property that is vertices that does contain all my vertices, but I cannot access them the way I am showing. enter image description here

Here is my full source code. The drawImage method will draw all the vertices correct, and push the vertex object into my geometry object. Then Once that is done I add the geometry to the particles and it works just fine, and I add it to the scene and get all my vertices displayed. Problem is I cannot access my vertices to manipulate them. So my particles object, drawImage and render method containing my for loop is where all this happens. In my render method it just says (TS) Property 'vertices' does not exists on type 'Geometry | buffer geometry'. It does exist and I show that with the image that my particles object which is a point object contains my geometry and that geometry has a property inside that has all my vertices called vertices.

import { Component, AfterViewInit, ElementRef, Input, ViewChild, HostListener} from '@angular/core';
import { TweenLite } from 'gsap';
import * as THREE from 'three';
declare const require: (moduleId: string) => any;
var OrbitControls = require('three-orbit-controls')(THREE);

@Component({
    selector: 'shared-background-scene',
    templateUrl: './backgroundScene.component.html',
    styleUrls: ['./backgroundScene.component.scss']
})

export class BackgroundSceneComponent {

    public scene: THREE.Scene;
    private renderer: THREE.WebGLRenderer;
    private camera: THREE.PerspectiveCamera;
    private cameraTarget: THREE.Vector3;
    public controls: THREE.OrbitControls;

    public fieldOfView: number = 60;
    public nearClippingPane: number = 1;
    public farClippingPane: number = 1100;

    @ViewChild('canvas')
    private canvasRef: ElementRef;

    public particles: THREE.Points;
    public imageData: any;
    public geometry = new THREE.Geometry();
    public material = new THREE.PointsMaterial({
                size: 3,
                color: 0x313742,
                sizeAttenuation: false
    });
    public loadImage() {
        //load texture, which is the image, and then get the imagedata, and draw image.
        var texture = new THREE.TextureLoader().load("assets/img/example-clouds.png", () => { console.log(texture); this.imageData = this.getImageData(texture.image); this.drawImage(); this.startRendering(); })
    }

    public getImageData(image: any) {
        console.log(image);
        // Create canvas for the image
        let canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.width;

        // Make sure context is 2d
        let context = canvas.getContext("2d");
        context!.drawImage(image, 0, 0);

        //return the context to be saved to the imageData.
        return context!.getImageData(0, 0, image.width, image.height);
    }

    public drawImage() {

        // Create vertices to draw the image.
        for (var y = 0, y2 = this.imageData.height; y < y2; y += 2) {
            for (var x = 0, x2 = this.imageData.width; x < x2; x += 2) {
                if (this.imageData.data[(x * 4 + y * 4 * this.imageData.width) + 3] > 128) {

                    let vertex:any = new THREE.Vector3();
                    vertex.x = Math.random() * 1000 - 500;
                    vertex.y = Math.random() * 1000 - 500;
                    vertex.z = -Math.random() * 500;

                    vertex.destination = {
                        x: x - this.imageData.width / 2,
                        y: -y + this.imageData.height / 2,
                        z: 0
                    };

                    vertex.speed = Math.random() / 200 + 0.015;

                    this.geometry.vertices.push(vertex);
                }
            }
        }

        console.log(this.geometry);
        this.particles = new THREE.Points(this.geometry, this.material);
        this.scene.add(this.particles);
        console.log(this.particles);
        requestAnimationFrame(this.render);
    }

    constructor() {
        this.render = this.render.bind(this);
    }

    private get canvas(): HTMLCanvasElement {
        return this.canvasRef.nativeElement;
    }

    private createScene() {
        this.scene = new THREE.Scene();
    }

    private createCamera() {
        let aspectRatio = this.getAspectRatio();
        this.camera = new THREE.PerspectiveCamera(
            this.fieldOfView,
            aspectRatio,
            this.nearClippingPane,
            this.farClippingPane
        );

        // Set position and look at
        this.camera.position.x = 10;
        this.camera.position.y = 10;
        this.camera.position.z = 100;
    }

    private getAspectRatio(): number {
        let height = this.canvas.clientHeight;
        if (height === 0) {
            return 0;
        }
        return this.canvas.clientWidth / this.canvas.clientHeight;
    }

    private startRendering() {
        this.renderer = new THREE.WebGLRenderer({
            canvas: this.canvas,
            antialias: true
        });
        this.renderer.setPixelRatio(devicePixelRatio);
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);

        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        this.renderer.setClearColor(0xffffff, 1);
        this.renderer.autoClear = true;

        let component: BackgroundSceneComponent = this;

        (function render() {
            requestAnimationFrame(render);
            component.render();
        }());
    }

    public render() {
        this.renderer.render(this.scene, this.camera);

        for (var i = 0, j = this.particles.geometry.vertices; i < j; i++) {
            var particle = this.particles.geometry.vertices[i];
            particle.x += (particle.destination.x - particle.x) * particle.speed;
            particle.y += (particle.destination.y - particle.y) * particle.speed;
            particle.z += (particle.destination.z - particle.z) * particle.speed;
        }
        this.particles.geometry.verticesneedupdate = true;
    }

    public addControls() {
        this.controls = new OrbitControls(this.camera);
        this.controls.rotateSpeed = 1.0;
        this.controls.zoomSpeed = 1.2;
        this.controls.addEventListener('change', this.render);

    }

    private loadCubeModel() {
        var geometry = new THREE.BoxGeometry(1, 1, 1);
        var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        var cube = new THREE.Mesh(geometry, material);
        this.scene.add(cube);
    }

    /* Events */
   private onResize(event: Event) {
        this.canvas.style.width = "100%";
        this.canvas.style.height = "100vh";
        //console.log("onResize: " + this.canvas.clientWidth + ", " + this.canvas.clientHeight);

        this.camera.aspect = this.getAspectRatio();
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
        this.render();
    }

    /* LIFECYCLE */
   ngAfterViewInit() {
        this.createScene();
        this.createCamera();
        this.loadImage(); // rendering starts here
        this.loadCubeModel();
        this.addControls();
    }
}

Quick example of me using console.log(this.particles.geometry.vertices.length); and getting the correct output, but it still says it does not exist. enter image description here

2
  • Can you add a console.log(yourmethodname) in each method that is executed and trace the execution order , I think there is bad execution order, your code is a but huge, hard to tell easily like that... Commented Jan 24, 2018 at 0:36
  • I fixed it, but I am just having problems with it saying I am getting an error when everything is executing as it should. Commented Jan 24, 2018 at 0:38

2 Answers 2

1

The reason is that, when we use THREE.Points, TypeScript have no way to know whether we'll use them with Geometry or with BufferGeometry. I'd just add this line in your render() method:

const geometry = this.particles.geometry as Geometry

and used geometry instead of this.particles.geometry in the following code. This will essentially tell TypeScript "I know, that it will always be Geometry, trust me".

Sign up to request clarification or add additional context in comments.

Comments

0

I changed my public particles = THREE.Points; -> public particles:any = THREE.Points;

This fixed it for me, but I just do not have types, and that sucks, but if you know what you are changing then you should be fine.

Fixed code:

import { Component, AfterViewInit, ElementRef, Input, ViewChild, HostListener} from '@angular/core';
import { TweenLite } from 'gsap';
import * as THREE from 'three';
declare const require: (moduleId: string) => any;
var OrbitControls = require('three-orbit-controls')(THREE);

@Component({
    selector: 'shared-background-scene',
    templateUrl: './backgroundScene.component.html',
    styleUrls: ['./backgroundScene.component.scss']
})

export class BackgroundSceneComponent {

    public scene: THREE.Scene;
    private renderer: THREE.WebGLRenderer;
    private camera: THREE.PerspectiveCamera;
    private cameraTarget: THREE.Vector3;
    public controls: THREE.OrbitControls;

    public fieldOfView: number = 60;
    public nearClippingPane: number = 1;
    public farClippingPane: number = 1100;

    @ViewChild('canvas')
    private canvasRef: ElementRef;

    public particles:any = THREE.Points;
    public imageData: any;
    public geometry = new THREE.Geometry();
    public material = new THREE.PointsMaterial({
                size: 3,
                color: 0x313742,
                sizeAttenuation: false
    });
    public loadImage() {
        //load texture, which is the image, and then get the imagedata, and draw image.
        var texture = new THREE.TextureLoader().load("assets/img/transparentMap.png", () => { console.log(texture); this.imageData = this.getImageData(texture.image); this.drawImage(); this.startRendering(); })
    }

    public getImageData(image: any) {
        console.log(image);
        // Create canvas for the image
        let canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.width;

        // Make sure context is 2d
        let context = canvas.getContext("2d");
        context!.drawImage(image, 0, 0);

        //return the context to be saved to the imageData.
        return context!.getImageData(0, 0, image.width, image.height);
    }

    public drawImage() {

        // Create vertices to draw the image.
        for (var y = 0, y2 = this.imageData.height; y < y2; y += 2) {
            for (var x = 0, x2 = this.imageData.width; x < x2; x += 2) {
                if (this.imageData.data[(x * 4 + y * 4 * this.imageData.width) + 3] > 128) {

                    let vertex:any = new THREE.Vector3();
                    vertex.x = Math.random() * 1000 - 500;
                    vertex.y = Math.random() * 1000 - 500;
                    vertex.z = -Math.random() * 500;

                    vertex.destination = {
                        x: x - this.imageData.width / 2,
                        y: -y + this.imageData.height / 2,
                        z: 0
                    };

                    vertex.speed = Math.random() / 200 + 0.015;

                    this.geometry.vertices.push(vertex);
                }
            }
        }
        this.particles = new THREE.Points(this.geometry, this.material);
        this.scene.add(this.particles);

        requestAnimationFrame(this.render);
    }

    constructor() {
        this.render = this.render.bind(this);
    }

    private get canvas(): HTMLCanvasElement {
        return this.canvasRef.nativeElement;
    }

    private createScene() {
        this.scene = new THREE.Scene();
    }

    private createCamera() {
        let aspectRatio = this.getAspectRatio();
        this.camera = new THREE.PerspectiveCamera(
            this.fieldOfView,
            aspectRatio,
            this.nearClippingPane,
            this.farClippingPane
        );

        // Set position and look at
        this.camera.position.x = 10;
        this.camera.position.y = 10;
        this.camera.position.z = 100;
    }

    private getAspectRatio(): number {
        let height = this.canvas.clientHeight;
        if (height === 0) {
            return 0;
        }
        return this.canvas.clientWidth / this.canvas.clientHeight;
    }

    private startRendering() {
        this.renderer = new THREE.WebGLRenderer({
            canvas: this.canvas,
            antialias: true
        });
        this.renderer.setPixelRatio(devicePixelRatio);
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);

        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        this.renderer.setClearColor(0xffffff, 1);
        this.renderer.autoClear = true;

        let component: BackgroundSceneComponent = this;

        (function render() {
            requestAnimationFrame(render);
            component.render();
        }());
    }

    public render() {
        this.renderer.render(this.scene, this.camera);

        // Draw image to screen
        for (var i = 0, j = this.particles.geometry.vertices.length; i < j; i++) {
            var particle:any = this.particles.geometry.vertices[i];
            particle.x += (particle.destination.x - particle.x) * particle.speed;
            particle.y += (particle.destination.y - particle.y) * particle.speed;
            particle.z += (particle.destination.z - particle.z) * particle.speed;
        }
        this.particles.geometry.verticesNeedUpdate = true;
    }

    public addControls() {
        this.controls = new OrbitControls(this.camera);
        this.controls.rotateSpeed = 1.0;
        this.controls.zoomSpeed = 1.2;
        this.controls.addEventListener('change', this.render);

    }

    private loadCubeModel() {
        var geometry = new THREE.BoxGeometry(1, 1, 1);
        var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        var cube = new THREE.Mesh(geometry, material);
        this.scene.add(cube);
    }

    /* Events */
   private onResize(event: Event) {
        this.canvas.style.width = "100%";
        this.canvas.style.height = "100vh";
        //console.log("onResize: " + this.canvas.clientWidth + ", " + this.canvas.clientHeight);

        this.camera.aspect = this.getAspectRatio();
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
        this.render();
    }

    /* LIFECYCLE */
   ngAfterViewInit() {
        this.createScene();
        this.createCamera();
        this.loadImage(); // rendering starts here
        this.loadCubeModel();
        this.addControls();
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.