#Extending multiple classes

1 messages · Page 1 of 1 (latest)

finite parcel
#

I have a class called Shape

abstract class Shape {
    transform: Matrix = identity();
    material: Material = material();

    setTransform(t: Matrix) {
        this.transform = t;
    }

    intersect(ray: Ray): Intersections {
        return this.localIntersect(ray.transform(this.transform.inverse()));
    }

    normalAt(point: Tuple): Tuple {
        const localNormal = this.localNormalAt(this.transform.inverse().multiplyTuple(point));
        const worldNormal = this.transform.inverse().transpose().multiplyTuple(localNormal);
        return makeVector(worldNormal.x, worldNormal.y, worldNormal.z).normalize();
    }

    protected abstract localIntersect(localRay: Ray): Intersections;

    protected abstract localNormalAt(localPoint: Tuple): Tuple;
}

export default Shape;

and another class called group:

export default class Group extends Shape {

    items: Shape[];

    constructor(...shapes: Shape[]) {
        super();
        this.items = shapes;
    }

    protected localIntersect(localRay: Ray): Intersections {
        return undefined;
    }

    protected localNormalAt(localPoint: Tuple): Tuple {
        return undefined;
    }

}

export const makeGroup = (...shapes: Shape[]) => {
    return new Group(...shapes);
};

I'm currently creating an items to store a list of shapes, however, I'd rather I have the class itself behave like an array, however I am unable to use both Array<Shape> and <Shape> (js only lets you extend a single class). I was wondering if it is possible to do this an alternative way?

worthy zealot
#

You can use TS mixins

#

!hb mix

short bisonBOT
latent ember
#

@finite parcel Usually there's probably some better pattern than multiple inheritance.

#

Like, instead of extending Array, I'd consider making the class iterable.

finite parcel
#

hmm, I've did try with using mixins, though couldn't quite get my head around how it worked. Specifically when one of the classes was an Array.

#

but using iterable seems simpler.

worthy zealot
#

I've never tried extending Array. But I think you can make one normal extension before getting into mixins

#

I kind of don't like them, I've never wanted to use them.

finite parcel
#

this is how my other class extends array

export default class Intersections extends Array<Intersection> {
    constructor(...its: Intersection[]) {
        super(...its);
    }

    hit(): Intersection | null {
        return this.reduce<Intersection | null>((closest, intersection) => {
            if (intersection.t > 0 && (closest === null || intersection.t < closest.t)) {
                return intersection;
            }
            return closest;
        }, null);
    }
}
latent ember
#

Here's how you can make something iterable without extending Array

class MyThing implements Iterable<number> {
  items = [1,2,3];
  [Symbol.iterator]() { return this.items.values() }
}

const stuff = new MyThing()
for(const x of stuff) {
  console.log(x); // 1, 2, 3
} 
finite parcel
#

I see. Hmm, can you do stuff.push()?

#

or iterator equivalent

latent ember
#

Iteration is read-only, so if you did need to support mutation you'd add methods for it:

class MyThing implements Iterable<number> {
  items = [1,2,3];
  [Symbol.iterator]() { return this.items.values() }
  push(item: number) { this.items.push(item); }
}
finite parcel
#

so just some standard methods to make it array-like

latent ember
#

Yeah - I assume you're extending array because you want to somehow control how it's modified anyway.