fillTriangle(v0, v1, v2, color0, color1, color2) {
            const { width, height, imageBuffer, depthBuffer } = this;
        
            // Combine vertices with their associated colors
            let vertices = [
                { pos: v0, color: color0 },
                { pos: v1, color: color1 },
                { pos: v2, color: color2 },
            ];
        
            // Sort the vertices by y-coordinate ascending (sv0.y <= sv1.y <= sv2.y)
            vertices.sort((a, b) => a.pos[1] - b.pos[1]);
        
            // Unpack the sorted vertices and colors
            const [sv0, sv1, sv2] = vertices.map(v => v.pos);
            const [sc0, sc1, sc2] = vertices.map(v => v.color);
        
            // Precompute the denominator for barycentric coordinates
            const denom = (sv1[1] - sv2[1]) * (sv0[0] - sv2[0]) + (sv2[0] - sv1[0]) * (sv0[1] - sv2[1]);
            if (denom === 0) return; // Degenerate triangle
        
            const invDenom = 1 / denom;
        
            // Determine the bounding box of the triangle
            const yStart = Math.max(0, Math.ceil(sv0[1]));
            const yEnd = Math.min(height - 1, Math.floor(sv2[1]));
        
            // Define the triangle edges for intersection tests
            const edges = [
                { v1: sv0, v2: sv1 },
                { v1: sv1, v2: sv2 },
                { v1: sv2, v2: sv0 }
            ];
        
            for (let y = yStart; y <= yEnd; y++) {
                let xMin = width, xMax = 0;
        
                // Find intersection points of the current scanline with the triangle edges
                for (const edge of edges) {
                    const { v1, v2 } = edge;
                    if (y < Math.min(v1[1], v2[1]) || y > Math.max(v1[1], v2[1])) continue;
        
                    const dy = v2[1] - v1[1];
                    if (dy === 0) continue; // Skip horizontal edges
        
                    const t = (y - v1[1]) / dy;
                    const x = v1[0] + t * (v2[0] - v1[0]);
        
                    xMin = Math.min(xMin, x);
                    xMax = Math.max(xMax, x);
                }
        
                // Clamp the x boundaries to the screen dimensions
                const xStart = Math.max(0, Math.ceil(xMin));
                const xEnd = Math.min(width - 1, Math.floor(xMax));
        
                // Precompute the base index for the current scanline in the buffer
                const baseIndex = y * width;
        
                for (let x = xStart; x <= xEnd; x++) {
                    // Compute barycentric coordinates
                    const w0 = ((sv1[1] - sv2[1]) * (x - sv2[0]) + (sv2[0] - sv1[0]) * (y - sv2[1])) * invDenom;
                    const w1 = ((sv2[1] - sv0[1]) * (x - sv2[0]) + (sv0[0] - sv2[0]) * (y - sv2[1])) * invDenom;
                    const w2 = 1 - w0 - w1;
        
                    // Early rejection if the point is outside the triangle
                    if (w0 < 0 || w1 < 0 || w2 < 0) continue;
        
                    // Depth interpolation (assuming z is at index 2)
                    const zInterpolated = 1 / (w0 / sv0[2] + w1 / sv1[2] + w2 / sv2[2]);
        
                    // Buffer index for depth and color
                    const index = baseIndex + x;
        
                    if (zInterpolated < depthBuffer[index]) {
                        depthBuffer[index] = zInterpolated;
        
                        // Perspective-correct color interpolation
                        const interpolatedColor = (w0 * sc0 + w1 * sc1 + w2 * sc2) / (w0 + w1 + w2);
        
                        const shadedColor = [
                            interpolatedColor,
                            interpolatedColor,
                            interpolatedColor,
                            255
                        ];
        
                        const bufferIndex = index * 4;
                        imageBuffer.data[bufferIndex] = shadedColor[0];
                        imageBuffer.data[bufferIndex + 1] = shadedColor[1];
                        imageBuffer.data[bufferIndex + 2] = shadedColor[2];
                        imageBuffer.data[bufferIndex + 3] = shadedColor[3];
                    }
                }
            }
        }

    }


Optimize this code as much as possible. Remove redundancies. Work with arrays instead of JSON if that is faster.