public static double[,] ConvertToCamera(double[,] pontos, Vector VRP, Vector P)
        {
            Vector bigN = new Vector();
            bigN.X = VRP.X - P.X;
            bigN.Y = VRP.Y - P.Y;
            bigN.Z = VRP.Z - P.Z;
            Vector vectorN = NormalizeVector(bigN);

            Vector Y = new Vector(0, 1, 0);
            Vector n1 = new Vector();
            double aux = DotProduct(Y, vectorN);
            n1.X = aux*vectorN.X;
            n1.Y = aux*vectorN.Y;
            n1.Z = aux*vectorN.Z;
            Vector v = new Vector(Y.X - n1.X, Y.Y - n1.Y, Y.Z - n1.Z);
            v = NormalizeVector(v);

            Vector u = new Vector();
            u = CrossProduct(v, vectorN);

            double[,] M = new double[,] {
                {u.X, u.Y, u.Z, -1*DotProduct(VRP, u)},
                {v.X, v.Y, v.Z, -1*DotProduct(VRP, v)},
                {vectorN.X, vectorN.Y, vectorN.Z, -1*DotProduct(VRP, vectorN)},
                {0, 0, 0, 1}
            };

            return MatrixMultiplication(M, pontos);
        }
 public static Vector CrossProduct(Vector a, Vector b)
 {
     var x = new Vector(a.Y * b.Z - a.Z * b.Y,
                           a.Z * b.X - b.Z * a.X,
                           a.X * b.Y - a.Y * b.X);
     x = NormalizeVector(x);
     return x;
 }
        public Solid()
        {
            //cria um solido padrao
            _points = new double[,] {
                {0,0,50,1},
                {50,0,50,1},
                {50,50,50,1},
                {0,50,50,1},
                {0,0,0,1},
                {50,0,0,1},
                {50,50,0,1},
                {0,50,0,1},
                {25,25,25,1}
            };
            _edges = new[,] {
                {0,1},
                {1,2},
                {2,3},
                {3,0},
                {1,5},
                {5,6},
                {6,2},
                {5,4},
                {4,7},
                {7,6},
                {4,0},
                {3,7}
            };
            Faces = new[,] {
                {0,1,2,3},
                {1,5,6,2},
                {5,4,7,6},
                {0,3,7,4},
                {3,2,6,7},
                {0,4,5,1}
            };

            FlatColor = new Vector[6];
            _points = MathOperations.TransposeMatrix(_points);

            _ka = new Vector(0.3, 0.5, 0.2);
            _ks = new Vector(0.9, 0.4, 0.1);
            _kd = new Vector(0.1, 0.8, 0.3);
            n = 4;
        }
 public static double DotProduct(Vector a, Vector b)
 {
     return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
 }
 public static Vector NormalizeVector(Vector x)
 {
     var module = Math.Sqrt(x.X * x.X + x.Y * x.Y + x.Z * x.Z);
     return new Vector(x.X / module, x.Y / module, x.Z / module);
 }
        private void MainView_Load(object sender, EventArgs e)
        {
            solidsList = new List<List<Solid>>();   //Aloca a lista de solido
            perspectiveSolidList = new List<List<Solid>>();

            selectedGuy = -1;   //configura o solido selecionado para: nenhum
            selectedSolids = new List<int>();

            amIRotating = false;

            P = new Vector(30, 20, 50);
            VRP = new Vector(300, 500, 3000);
            Y = new Vector(0, 1, 0);
            VRPiso = new Vector(300, 300, 300);
            alpha = 110;

            L = new Vector(100, 10, 150);
            iL = new Vector(150, 100, 250);
            iLa = new Vector(20, 80, 50);

            xMin = yMin = 0;
            xMax = yMax = 315;
        }
        public void ComputeVisibility(Vector vrp, Vector P =null)
        {
            //processa cada face e determina sua visibilidade
            var n = new Vector();   //vetor vrp - p
            n.X = vrp.X - (P == null?Points[0, 8]:P.X);
            n.Y = vrp.Y - (P == null?Points[1, 8]:P.Y);
            n.Z = vrp.Z - (P == null?Points[2, 8]:P.Z);
            n = MathOperations.NormalizeVector(n);
            for (int i = 0; i < 6; i++) {   //processa as faces uma a uma
                var vec1 = new Vector(_points[0, Faces[i, 1]] - _points[0, Faces[i, 0]],
                    _points[1, Faces[i, 1]] - _points[1, Faces[i, 0]],
                    _points[2, Faces[i, 1]] - _points[2, Faces[i, 0]]);    //vetor 1 da face
                var vec2 = new Vector(_points[0, Faces[i, 2]] - _points[0, Faces[i, 1]],
                    _points[1, Faces[i, 2]] - _points[1, Faces[i, 1]],
                    _points[2, Faces[i, 2]] - _points[2, Faces[i, 1]]);    //vetor 2 da face
                var face = MathOperations.CrossProduct(vec1, vec2);    //vetor normal a face

                face = MathOperations.NormalizeVector(face);

                var visibilityK = MathOperations.DotProduct(face, n);   //coeficiente de visibilidade
                if (visibilityK > 0) {
                    _visibleFaces[i] = true;
                    normalFaces[i] = face;
                } else {
                    _visibleFaces[i] = false;
                    normalFaces[i] = face;
                }

            }
        }
        private void PerspectiveTransformation() {

            #region Camera Transformations
            perspectiveSolidList = solidsList.Clone();
            var bigN = new Vector {
                X = VRP.X - P.X,
                Y = VRP.Y - P.Y,
                Z = VRP.Z - P.Z
            };
            var n = MathOperations.NormalizeVector(bigN);

            var n1 = new Vector();
            var aux = MathOperations.DotProduct(Y, n);
            n1.X = aux * n.X;
            n1.Y = aux * n.Y;
            n1.Z = aux * n.Z;
            var v = new Vector(Y.X - n1.X, Y.Y - n1.Y, Y.Z - n1.Z);
            v = MathOperations.NormalizeVector(v);

            var u = new Vector();
            u = MathOperations.CrossProduct(v, n);

            var M = new double[,] {
                {u.X, u.Y, u.Z, -1*MathOperations.DotProduct(VRP, u)},
                {v.X, v.Y, v.Z, -1*MathOperations.DotProduct(VRP, v)},
                {n.X, n.Y, n.Z, -1*MathOperations.DotProduct(VRP, n)},
                {0,0,0,1}
            };

            foreach (var solid in perspectiveSolidList.SelectMany(t => t)) {
                solid.Points = M.Multiply(solid.Points);
            }
            #endregion

            var dp = Math.Sqrt(Math.Pow(VRP.X - P.X, 2) +
                               Math.Pow(VRP.Y - P.Y, 2) +
                               Math.Pow(VRP.Z - P.Z, 2));
            //Transformacao perspectiva
            var perspecMatrix = new double[,] {
                {1,0,0,0},
                {0,1,0,0},
                {0,0,1,0},
                {0,0,-1/(dp*alpha/100.0),0},
            };
            foreach (var solid in perspectiveSolidList.SelectMany(t => t)) {
                solid.Points = MathOperations.MatrixMultiplication(perspecMatrix, solid.Points);
                for (var i = 0; i < solid.Points.GetLength(0); i++) {
                    for (var j = 0; j < solid.Points.GetLength(1); j++) {
                        solid.Points[i, j] /= solid.Points[solid.Points.GetLength(0) - 1, j];
                    }
                }
            }

        }
        public int DecideMid(Vector ponto1, Vector ponto2, Vector ponto3, Vector ponto4) {

            Vector[] aux = new Vector[]{ponto1, ponto2, ponto3, ponto4};
            int menor = (int)aux[0].Y;
            int j = 0;
            //Pega o menor
            for (int i = 0; i < 4; i++) {
                if (aux[i].Y < menor) {
                    menor = (int)aux[i].Y;
                    j = i;
                }
            }

            //Teste se alguem eh igual
            for (int i = 0; i < 4; i++) {
                if (aux[i].Y == menor && j != i) {
                    if (aux[i].X < aux[j].X)
                        return i;
                }
            }
            return j;
            /*if (ponto1.Y.CompareTo(ponto2.Y) <= 0) {
                if (ponto3.Y.CompareTo(ponto4) <= 0) {
                    return ponto1.Y <= ponto3.Y ? 0 : 2;
                }
                return ponto1.Y <= ponto4.Y ? 0 : 3;
            }
            if (ponto3.Y.CompareTo(ponto4) <= 0) {
                return ponto2.Y <= ponto3.Y ? 1 : 2;
            }
            return ponto2.Y <= ponto4.Y ? 1 : 3;*/

        }
        public void ZBuffer(int view, PictureBox pc) {
            switch (view) {
                case 0 : //Top view
                    MapToScreen(0);
                    break;
                case 1 : //Front view
                    MapToScreen(1);

                    #region Truncamento
                    foreach (var list in perspectiveSolidList) {
                        foreach (var solid in list) {
                            for (int i = 0; i < solid.Points.GetLength(0); i++) {
                                for (int j = 0; j < solid.Points.GetLength(1); j++) {
                                    solid.Points[i, j] = (int) solid.Points[i, j];
                                }
                            }
                        }
                    }
                    #endregion

                    foreach (var solid in perspectiveSolidList.SelectMany(solidList => solidList)) {

                        solid.ComputeVisibility(new Vector(solid.Points[0, 8], solid.Points[1, 8], 1));

                        for (int i = 0; i < 6; i++) {
                            if (!solid.VisibleFaces[i]) continue;
                            int yMenor =
                                DecideMid(
                                    new Vector(solid.Points[0, solid.Faces[i, 0]], solid.Points[1, solid.Faces[i, 0]],
                                        solid.Points[2, solid.Faces[i, 0]]),
                                    new Vector(solid.Points[0, solid.Faces[i, 1]], solid.Points[1, solid.Faces[i, 1]],
                                        solid.Points[2, solid.Faces[i, 1]]),
                                    new Vector(solid.Points[0, solid.Faces[i, 2]], solid.Points[1, solid.Faces[i, 2]],
                                        solid.Points[2, solid.Faces[i, 2]]),
                                    new Vector(solid.Points[0, solid.Faces[i, 3]], solid.Points[1, solid.Faces[i, 3]],
                                        solid.Points[2, solid.Faces[i, 3]]));

                            Vector mid = new Vector((int)solid.Points[0, solid.Faces[i, yMenor]],
                                (int)solid.Points[1, solid.Faces[i, yMenor]],
                                (int)solid.Points[2, solid.Faces[i, yMenor]]);

                            Vector prev = new Vector((int)solid.Points[0, solid.Faces[i, (yMenor + 1) % 4]],
                                (int)solid.Points[1, solid.Faces[i, (yMenor + 1) % 4]],
                                (int)solid.Points[2, solid.Faces[i, (yMenor + 1) % 4]]);

                            Vector next = new Vector((int)solid.Points[0, solid.Faces[i, (yMenor - 1 < 0 ? 3 : yMenor - 1)]],
                                    (int)solid.Points[1, solid.Faces[i, (yMenor - 1 < 0 ? 3 : yMenor - 1)]],
                                    (int)solid.Points[2, solid.Faces[i, (yMenor - 1 < 0 ? 3 : yMenor - 1)]]);

                            Vector end = new Vector((int)solid.Points[0, solid.Faces[i, (yMenor + 2) % 4]],
                                (int)solid.Points[1, solid.Faces[i, (yMenor + 2) % 4]],
                                (int)solid.Points[2, solid.Faces[i, (yMenor + 2) % 4]]);


                            double deltaX1 = 0;
                            double deltaX2 = 0;
                            double deltaZ1 = 0;
                            double deltaZ2 = 0;

                            if (!(mid.Y == prev.Y)) {
                                deltaX1 = GetDelta(mid.X, prev.X, mid.Y, prev.Y);
                                deltaZ1 = GetDelta(mid.Z, prev.Z, mid.Y, prev.Y);
                            }
                            if (!(mid.Y == next.Y)) {
                                deltaX2 = GetDelta(mid.X, next.X, mid.Y, next.Y);
                                deltaZ2 = GetDelta(mid.Z, next.Z, mid.Y, next.Y);
                            }
                            
                            Console.WriteLine("Delta x1: {0}", deltaX1);
                            Console.WriteLine("Delta z1: {0}", deltaZ1);
                            Console.WriteLine("Delta x2: {0}", deltaX2);
                            Console.WriteLine("Delta z2: {0}", deltaZ2);

                            
                            
                            int k1 = 1;
                            int k2 = 1;


                            for (int w = 0; w < (int)(end.Y-mid.Y); w++) {
                                Vector ponto1 = new Vector(mid.X+k1*deltaX1, mid.Y+k1, mid.Z+k1*deltaZ1); //MID TO PREV
                                Vector ponto2 = new Vector(mid.X+k2*deltaX2, mid.Y+k2, mid.Z+k2*deltaZ2); //MID TO NEXT

                                if (prev.Y - mid.Y == k1) {
                                    k1 = 0;
                                    deltaX1 = GetDelta(end.X, prev.X, end.Y, prev.Y);
                                    deltaZ1 = GetDelta(end.Z, prev.Z, end.Y, prev.Y);
                                }

                                if (next.Y - mid.Y == k2) {
                                    k2 = 0;
                                    deltaX2 = GetDelta(end.X, next.X, end.Y, next.Y);
                                    deltaZ2 = GetDelta(end.Z, next.Z, end.Y, next.Y);
                                }
                                double a = ponto1.X;
                                double b = ponto2.X;
                                Console.Write("Ponto1 ");
                                Console.WriteLine(a);
                                Console.Write("Ponto2 ");
                                Console.WriteLine(b);
                                Console.Write("Distancia ");
                                double distancia = a - b;
                                Console.WriteLine(distancia);

                                for (int j = (int)(ponto1.X - ponto2.X); j >= 0; j--) {
                                    Vector pontoCur = new Vector(ponto2.X+j, mid.Y+w, ((ponto1.Z - ponto2.Z)/(ponto1.X - ponto2.X)) * j + ponto2.Z);
                                    var g = Graphics.FromImage(pc.Image);
                                    g.SmoothingMode = SmoothingMode.AntiAlias;
                                    g.FillRectangle(new SolidBrush(Color.FromArgb(j,0,0)), (int)pontoCur.X, (int)pontoCur.Y, 1, 1);
                                }
                                k1++;
                                k2++;
                            }
                        }
                    }
                    break;

                case 2 : //Left view
                    MapToScreen(2);
                    break;
                case 3 : //Perspective
                    MapToScreen(3);
                    break;
            }
            
        }
        public void FlatShading(Solid solid, PictureBox pc) {
            Vector iT;
            Vector iA;
            Vector iD;
            Vector iS;
            Vector R;
            Vector S;
            Vector Pm;


            for (int i = 0; i < 6; i++) {

                iA = new Vector();
                iA.X = iLa.X * solid.Ka.X;
                iA.Y = iLa.Y * solid.Ka.Y;
                iA.Z = iLa.Z * solid.Ka.Z;

                if (solid.VisibleFaces[i] == false) {
                    solid.FlatColor[i] = null;
                    continue;
                }


                iD = new Vector(0, 0, 0);
                iS = new Vector(0, 0, 0);
                Vector N = solid.NormalFaces[i];
                Pm = new Vector();
                Pm.X = (solid.Points[0, solid.Faces[i, 0]] + solid.Points[0, solid.Faces[i, 2]]) / 2;
                Pm.Y = (solid.Points[1, solid.Faces[i, 0]] + solid.Points[1, solid.Faces[i, 2]]) / 2;
                Pm.Z = (solid.Points[2, solid.Faces[i, 0]] + solid.Points[2, solid.Faces[i, 2]]) / 2;
                L = new Vector(L.X - Pm.X, L.Y - Pm.Y, L.Z - Pm.Z);
                L = MathOperations.NormalizeVector(L);
                double value = MathOperations.DotProduct(N, L);

                if (value > 0) {
                    iD.X = iL.X * solid.Kd.X * value;
                    iD.Y = iL.Y * solid.Kd.Y * value;
                    iD.Z = iL.Z * solid.Kd.Z * value;

                    R = new Vector();
                    R.X = (2 * value * N.X) - L.X;
                    R.Y = (2 * value * N.Y) - L.Y;
                    R.Z = (2 * value * N.Z) - L.Z;

                    S = new Vector();

                    S.X = VRP.X - Pm.X;
                    S.Y = VRP.Y - Pm.Y;
                    S.Z = VRP.Z - Pm.Z;
                    S = MathOperations.NormalizeVector(S);

                    double value2 = Math.Pow(MathOperations.DotProduct(R, S), solid.N);
                    iS.X = iL.X * solid.Ks.X * value2;
                    iS.Y = iL.Y * solid.Ks.Y * value2;
                    iS.Z = iL.Z * solid.Ks.Z * value2;
                }

                iT = new Vector();
                iT.X = iA.X + iD.X + iS.X;
                iT.Y = iA.Y + iD.Y + iS.Y;
                iT.Z = iA.Z + iD.Z + iS.Z;


                solid.FlatColor[i] = new Vector(iT.X, iT.Y, iT.Z);
            }
        }
        private void IsometricTransformation() {
            //Coordenadas de tela

            perspectiveSolidList = solidsList.Clone();
            Vector bigN = new Vector();
            bigN.X = VRPiso.X;
            bigN.Y = VRPiso.Y;
            bigN.Z = VRPiso.Z;
            Vector n = MathOperations.NormalizeVector(bigN);


            Vector Y = new Vector(0, 1, 0);
            Vector n1 = new Vector();
            double aux = MathOperations.DotProduct(Y, n);
            n1.X = aux * n.X;
            n1.Y = aux * n.Y;
            n1.Z = aux * n.Z;
            Vector v = new Vector(Y.X - n1.X, Y.Y - n1.Y, Y.Z - n1.Z);
            v = MathOperations.NormalizeVector(v);

            Vector u = new Vector();
            u = MathOperations.CrossProduct(v, n);

            double[,] M = new double[,] {
                {u.X, u.Y, u.Z, -1*MathOperations.DotProduct(VRPiso, u)},
                {v.X, v.Y, v.Z, -1*MathOperations.DotProduct(VRPiso, v)},
                {n.X, n.Y, n.Z, -1*MathOperations.DotProduct(VRPiso, n)},
                {0,0,0,1}
            };
            foreach (var solid in perspectiveSolidList.SelectMany(t => t)) {
                solid.Points = MathOperations.MatrixMultiplication(M, solid.Points);
            }
        }