import '../../../assert/js/assert.js';
import '../../../globals.js';
import Vector2 from '../../../dot/js/Vector2.js';
import Vector4 from '../../../dot/js/Vector4.js';
import { alpenglow, RenderColor, RenderInstruction, RenderProgram } from '../imports.js';
// REVIEW: What does the accuracy do?
export var RenderBarycentricBlendAccuracy;
(function (RenderBarycentricBlendAccuracy) {
    RenderBarycentricBlendAccuracy[RenderBarycentricBlendAccuracy["Accurate"] = 0] = "Accurate";
    RenderBarycentricBlendAccuracy[RenderBarycentricBlendAccuracy["PixelCenter"] = 1] = "PixelCenter";
})(RenderBarycentricBlendAccuracy || (RenderBarycentricBlendAccuracy = {}));
const scratchCentroid = new Vector2(0, 0);
alpenglow.register('RenderBarycentricBlendAccuracy', RenderBarycentricBlendAccuracy);
export default class RenderBarycentricBlend extends RenderProgram {
    constructor(pointA, pointB, pointC, accuracy, a, b, c, logic) {
        assert && assert(pointA.isFinite());
        assert && assert(pointB.isFinite());
        assert && assert(pointC.isFinite());
        assert && assert(!pointA.equals(pointB));
        assert && assert(!pointB.equals(pointC));
        assert && assert(!pointC.equals(pointA));
        super([a, b, c], a.isFullyTransparent && b.isFullyTransparent && c.isFullyTransparent, a.isFullyOpaque && b.isFullyOpaque && c.isFullyOpaque, false, false, accuracy === RenderBarycentricBlendAccuracy.Accurate);
        this.pointA = pointA;
        this.pointB = pointB;
        this.pointC = pointC;
        this.accuracy = accuracy;
        this.a = a;
        this.b = b;
        this.c = c;
        this.logic = logic || RenderBarycentricBlendLogic.from(this.pointA, this.pointB, this.pointC, this.accuracy);
    }
    getName() {
        return 'RenderBarycentricBlend';
    }
    withChildren(children) {
        assert && assert(children.length === 3);
        return new RenderBarycentricBlend(this.pointA, this.pointB, this.pointC, this.accuracy, children[0], children[1], children[2], this.logic);
    }
    transformed(transform) {
        return new RenderBarycentricBlend(transform.timesVector2(this.pointA), transform.timesVector2(this.pointB), transform.timesVector2(this.pointC), this.accuracy, this.a.transformed(transform), this.b.transformed(transform), this.c.transformed(transform));
    }
    equalsTyped(other) {
        return this.pointA.equals(other.pointA) &&
            this.pointB.equals(other.pointB) &&
            this.pointC.equals(other.pointC) &&
            this.accuracy === other.accuracy;
    }
    getSimplified(children) {
        const a = children[0];
        const b = children[1];
        const c = children[2];
        if (a.isFullyTransparent && b.isFullyTransparent && c.isFullyTransparent) {
            return RenderColor.TRANSPARENT;
        }
        else if (a.equals(b) && a.equals(c)) {
            return a;
        }
        else {
            return null;
        }
    }
    evaluate(context) {
        const aColor = this.a.evaluate(context);
        const bColor = this.b.evaluate(context);
        const cColor = this.c.evaluate(context);
        const vector = new Vector4(0, 0, 0, 0);
        this.logic.apply(vector, context, aColor, bColor, cColor);
        return vector;
    }
    writeInstructions(instructions) {
        this.c.writeInstructions(instructions);
        this.b.writeInstructions(instructions);
        this.a.writeInstructions(instructions);
        instructions.push(new RenderInstructionBarycentricBlend(this.logic));
    }
    serialize() {
        return {
            type: 'RenderBarycentricBlend',
            pointA: [this.pointA.x, this.pointA.y],
            pointB: [this.pointB.x, this.pointB.y],
            pointC: [this.pointC.x, this.pointC.y],
            accuracy: this.accuracy,
            a: this.a.serialize(),
            b: this.b.serialize(),
            c: this.c.serialize()
        };
    }
    static deserialize(obj) {
        return new RenderBarycentricBlend(new Vector2(obj.pointA[0], obj.pointA[1]), new Vector2(obj.pointB[0], obj.pointB[1]), new Vector2(obj.pointC[0], obj.pointC[1]), obj.accuracy, RenderProgram.deserialize(obj.a), RenderProgram.deserialize(obj.b), RenderProgram.deserialize(obj.c));
    }
}
alpenglow.register('RenderBarycentricBlend', RenderBarycentricBlend);
export class RenderBarycentricBlendLogic {
    constructor(det, diffA, diffB, pointC, accuracy) {
        this.det = det;
        this.diffA = diffA;
        this.diffB = diffB;
        this.pointC = pointC;
        this.accuracy = accuracy;
        assert && assert(isFinite(det));
        assert && assert(diffA.isFinite());
        assert && assert(diffB.isFinite());
        assert && assert(pointC.isFinite());
    }
    static from(pointA, pointB, pointC, accuracy) {
        const pA = pointA;
        const pB = pointB;
        const pC = pointC;
        const det = (pB.y - pC.y) * (pA.x - pC.x) + (pC.x - pB.x) * (pA.y - pC.y);
        const diffA = new Vector2(pB.y - pC.y, pC.x - pB.x);
        const diffB = new Vector2(pC.y - pA.y, pA.x - pC.x);
        /*
        NOTES FOR THE FUTURE: Here were the original formulas
        const det = ( pB.y - pC.y ) * ( pA.x - pC.x ) + ( pC.x - pB.x ) * ( pA.y - pC.y );
        const lambdaA = ( ( pB.y - pC.y ) * ( point.x - pC.x ) + ( pC.x - pB.x ) * ( point.y - pC.y ) ) / det;
        const lambdaB = ( ( pC.y - pA.y ) * ( point.x - pC.x ) + ( pA.x - pC.x ) * ( point.y - pC.y ) ) / det;
         */
        return new RenderBarycentricBlendLogic(det, diffA, diffB, pointC, accuracy);
    }
    equals(other) {
        return Math.abs(this.det - other.det) < 1e-6 &&
            this.diffA.equalsEpsilon(other.diffA, 1e-6) &&
            this.diffB.equalsEpsilon(other.diffB, 1e-6) &&
            this.pointC.equalsEpsilon(other.pointC, 1e-6) &&
            this.accuracy === other.accuracy;
    }
    apply(output, context, aColor, bColor, cColor) {
        if (assert) {
            if (this.accuracy === RenderBarycentricBlendAccuracy.Accurate) {
                assert(context.hasCentroid());
            }
        }
        const point = this.accuracy === RenderBarycentricBlendAccuracy.Accurate ? context.centroid : context.writeBoundsCentroid(scratchCentroid);
        const pointC = this.pointC;
        const lambdaA = (this.diffA.x * (point.x - pointC.x) + this.diffA.y * (point.y - pointC.y)) / this.det;
        const lambdaB = (this.diffB.x * (point.x - pointC.x) + this.diffB.y * (point.y - pointC.y)) / this.det;
        const lambdaC = 1 - lambdaA - lambdaB;
        output.setXYZW(aColor.x * lambdaA + bColor.x * lambdaB + cColor.x * lambdaC, aColor.y * lambdaA + bColor.y * lambdaB + cColor.y * lambdaC, aColor.z * lambdaA + bColor.z * lambdaB + cColor.z * lambdaC, aColor.w * lambdaA + bColor.w * lambdaB + cColor.w * lambdaC);
    }
}
const scratchAColor = new Vector4(0, 0, 0, 0);
const scratchBColor = new Vector4(0, 0, 0, 0);
const scratchCColor = new Vector4(0, 0, 0, 0);
const scratchResult = new Vector4(0, 0, 0, 0);
export class RenderInstructionBarycentricBlend extends RenderInstruction {
    constructor(logic) {
        super();
        this.logic = logic;
    }
    toString() {
        const det = `det:${this.logic.det}`;
        const diffA = `diffA:${this.logic.diffA.x},${this.logic.diffA.y}`;
        const diffB = `diffB:${this.logic.diffB.x},${this.logic.diffB.y}`;
        const diffC = `pointC:${this.logic.pointC.x},${this.logic.pointC.y}`;
        return `RenderInstructionBarycentricBlend(${det} ${diffA} ${diffB} ${diffC})`;
    }
    equals(other, areLocationsEqual) {
        return other instanceof RenderInstructionBarycentricBlend && this.logic.equals(other.logic);
    }
    execute(stack, context, executor) {
        const aColor = stack.popInto(scratchAColor);
        const bColor = stack.popInto(scratchBColor);
        const cColor = stack.popInto(scratchCColor);
        this.logic.apply(scratchResult, context, aColor, bColor, cColor);
        stack.push(scratchResult);
    }
    writeBinary(encoder, getOffset) {
        encoder.pushU32(RenderInstruction.BarycentricBlendCode | (this.logic.accuracy << 8)); // 0
        encoder.pushF32(this.logic.det); // 1
        encoder.pushF32(this.logic.diffA.x); // 2
        encoder.pushF32(this.logic.diffA.y); // 3
        encoder.pushF32(this.logic.diffB.x); // 4
        encoder.pushF32(this.logic.diffB.y); // 5
        encoder.pushF32(this.logic.pointC.x); // 6
        encoder.pushF32(this.logic.pointC.y); // 7
    }
    static fromBinary(encoder, offset, getLocation) {
        const accuracy = (encoder.fullU32Array[offset] >> 8) & 0xff;
        const det = encoder.fullF32Array[offset + 1];
        const diffA = new Vector2(encoder.fullF32Array[offset + 2], encoder.fullF32Array[offset + 3]);
        const diffB = new Vector2(encoder.fullF32Array[offset + 4], encoder.fullF32Array[offset + 5]);
        const pointC = new Vector2(encoder.fullF32Array[offset + 6], encoder.fullF32Array[offset + 7]);
        return new RenderInstructionBarycentricBlend(new RenderBarycentricBlendLogic(det, diffA, diffB, pointC, accuracy));
    }
    getBinaryLength() {
        return 8;
    }
}
//# sourceMappingURL=RenderBarycentricBlend.js.map