using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;

namespace TelerikTest
{
    public class Wall
    {  
        public double L { get; set; }
        public double b { get; set; }
        public double CGX { get; set; }
        public double CGY { get; set; }
        public Point Pi { get; set; }
        public Point Pj { get; set; }
        public List<Point3D> Geom { get; set; }
        public double LI { get; set; }
        public double LJ { get; set; }
        public double LcI { get; set; }
        public double LcJ { get; set; }

        public bool IsSyncIJ { get; set; }

        public int[] PhisI { get; set; }
        public int[] PhisJ { get; set; }

        private int[] NbcsI;
        private int[] NbcsJ;

        public int NbcI
        {
            get
            {
                return _NbcI;
            }
            set
            {
                if (value < 1)
                    value = 1;

                _NbcI = value;
                for (int i = 0; i < 20; i++)
                    NbcsI[i] = _NbcI;

                if (IsSyncIJ)
                {
                    NbcJ = value;
                }

            }

        }
        public int NbcJ
        {
            get
            {
                return _NbcJ;
            }
            set
            {
                if (value < 1)
                    value = 1;

                _NbcJ = value;
                for (int i = 0; i < 20; i++)
                    NbcsJ[i] = _NbcJ;
            }

        }

        private int _NbcI;
        private int _NbcJ;

        public double[] DlsI { get; set; }
        public double[] DlsJ { get; set; }

        public double[] DxsI { get; set; }
        public double[] DxsJ { get; set; }

        public int fmv { get; set; }
        public int smv { get; set; }
        public int nmv { get; set; }

        public int fmh { get; set; }
        public int smh { get; set; }
        public int nmh { get; set; }

        public int fEI { get; set; }
        public int fTcI { get; set; }
        public int fTlI { get; set; }
        public int sEI { get; set; }
        public int nCEI { get; set; }
        public int nCEI_User { get; set; }
        public int nTcI { get; set; }
        public int nTlI { get; set; }
        public int nCTlI { get; set; }

        public int fEJ { get; set; }
        public int fTcJ { get; set; }
        public int fTlJ { get; set; }
        public int sEJ { get; set; }
        public int nCEJ { get; set; }
        public int nCEJ_User { get; set; }
        public int nTcJ { get; set; }
        public int nTlJ { get; set; }
        public int nCTlJ { get; set; }

        public static double LargoPata = 0.08;
        public static double AxAn = 0;

        //Geometria de armadura
        public List<Rebar> BarrasI { get; set; }
        public List<Rebar> BarrasJ { get; set; }
        public List<Rebar> BarrasMV { get; set; }
        public List<Rebar> BarrasMH { get; set; }

        public enum PuntaMuro { I, J }

        public Wall(double L, double b, double CGX, double CGY)
        {
            this.L = L;
            this.b = b;
            this.CGX = CGX;
            this.CGY = CGY;

            double cosAx = 1;
            double sinAx = 0;

            Pi = new Point(CGX - L / 2 * cosAx, CGY - L / 2 * sinAx);
            Pj = new Point(CGX + L / 2 * cosAx, CGY + L / 2 * sinAx);

            Geom = new List<Point3D>
            {
                new Point3D(Pi.X + b / 2 * sinAx, Pi.Y - b / 2 * cosAx, 0),
                new Point3D(Pj.X + b / 2 * sinAx, Pj.Y - b / 2 * cosAx, 0),
                new Point3D(Pj.X - b / 2 * sinAx, Pj.Y + b / 2 * cosAx, 0),
                new Point3D(Pi.X - b / 2 * sinAx, Pi.Y + b / 2 * cosAx, 0),
                new Point3D(Pi.X + b / 2 * sinAx, Pi.Y - b / 2 * cosAx, 0),
                new Point3D(Pj.X + b / 2 * sinAx, Pj.Y - b / 2 * cosAx, 0),
                new Point3D(Pj.X - b / 2 * sinAx, Pj.Y + b / 2 * cosAx, 0),
                new Point3D(Pi.X - b / 2 * sinAx, Pi.Y + b / 2 * cosAx, 0)
            };

            BarrasI = new List<Rebar>();
            BarrasJ = new List<Rebar>();
            BarrasMV = new List<Rebar>();
            BarrasMH = new List<Rebar>();

            IsSyncIJ = true;

            PhisI = new int[20] { 25, 25, 25, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            PhisJ = new int[20] { 25, 25, 25, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            NbcsI = new int[20] { 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            NbcsJ = new int[20] { 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            NbcI = 3;
            NbcJ = 3;

            DlsI = new double[20];
            DlsJ = new double[20];

            DxsI = new double[20];
            DxsJ = new double[20];

            DlsI[0] = 0.05; //La primera barra esta a 5 cm del borde (RecLong)
            DlsJ[0] = 0.05;

            double Dx = 0.05;
            DxsI[0] = Dx;
            DxsJ[0] = Dx;

            for (int i = 1; i < 20; i++)
            {
                DlsI[i] = 0.15; //La barra 2 a la 20 estan a 10 cm una de la otra
                DlsJ[i] = 0.15;

                Dx = Dx + 0.15;
                DxsI[i] = Dx;
                DxsJ[i] = Dx;
            }

            GenBarrasPuntaI();
            GenBarrasPuntaJ();

            nmh = 2;
            fmh = 16;
            smh = 10;
            GenBarrasMH();

            nmv = 2;
            fmv = 16;
            smv = 15;
            GenBarrasMV();

            fEI = 12;
            fTcI = 12;
            fTlI = 12;
            sEI = 10;
            nCEI = 4;
            nCEI_User = 4;

            GenConfinamientoPuntaI();
            GenConfinamientoPuntaJ();

        }

        public void GenBarrasPuntaI()
        {
            //Verificar que exista al menos una barra
            if (PhisI.All(phi => phi == 0) || PhisI[0] == 0)
            {
                PhisI[0] = 16;
            }

            if (PhisI.Any(phi => phi < 0))
            {
                for (int i = 0; i < 20; i++)
                {
                    if (PhisI[i] < 0)
                        PhisI[i] = i == 0 ? 16 : 0;
                }
            }


            BarrasI = new List<Rebar>();

            double Ax = AxAn / 180 * Math.PI;

            double rect = 0.035;

            double Dl = 0;

            for (int i = 0; i < 20; i++)
            {
                if (PhisI[i] > 0)
                {
                    Dl = Dl + DlsI[i];

                    if (Dl > (L / 2 - 0.05)) //Si la posición de la nueva barra supera la mitad del muro
                    {
                        Dl = Dl - DlsI[i];
                        for (int j = i; j < 20; j++)
                        {
                            PhisI[j] = 0;
                        }
                        break;
                    }

                    else
                    {
                        for (int j = 0; j < NbcsI[i]; j++)
                        {
                            double x;
                            double y;

                            if (NbcsI[i] == 1)
                            {
                                x = Pi.X + Math.Cos(Ax) * Dl;
                                y = Pi.Y + Math.Sin(Ax) * Dl;
                            }

                            else
                            {
                                double Dt = (b - 2 * rect) / (Math.Max(NbcsI[i], 2) - 1);

                                x = Pi.X + Math.Cos(Ax) * Dl + Math.Cos(Ax - Math.PI / 2) * (b / 2 - rect) + Math.Cos(Ax + Math.PI / 2) * j * Dt;
                                y = Pi.Y + Math.Sin(Ax) * Dl + Math.Sin(Ax - Math.PI / 2) * (b / 2 - rect) + Math.Sin(Ax + Math.PI / 2) * j * Dt;
                            }


                            BarrasI.Add(new Rebar(PhisI[i], new Point3D(x, y, 0)));
                        }
                    }
                }
                else
                {
                    for (int j = i; j < 20; j++)
                    {
                        PhisI[j] = 0;
                    }
                    break;
                }
            }

            if (NbcsI.All(n => n == 1))
                GenBarrasMH();

            LI = Dl;

            //Punta J
            if (IsSyncIJ)
                CopiarArmaduraPuntaIaJ();
        }

        public void GenBarrasPuntaJ()
        {
            //Verificar que exista al menos una barra
            if (PhisJ.All(phi => phi == 0) || PhisJ[0] == 0)
            {
                PhisJ[0] = 16;
            }

            if (PhisJ.Any(phi => phi < 0))
            {
                for (int i = 0; i < 20; i++)
                {
                    if (PhisJ[i] < 0)
                        PhisJ[i] = i == 0 ? 16 : 0;
                }
            }

            BarrasJ = new List<Rebar>();

            double Ax = AxAn / 180 * Math.PI;

            double rect = 0.035;

            double Dl = 0;

            for (int i = 0; i < 20; i++)
            {
                if (PhisJ[i] > 0)
                {
                    Dl = Dl + DlsJ[i];

                    if (Dl > (L / 2 - 0.05)) //Si la posición de la nueva barra supera la mitad del muro
                    {
                        Dl = Dl - DlsJ[i];
                        for (int j = i; j < 20; j++)
                        {
                            PhisJ[j] = 0;
                        }
                        break;
                    }

                    else
                    {
                        for (int j = 0; j < NbcsJ[i]; j++)
                        {
                            double x;
                            double y;

                            if (NbcsJ[i] == 1)
                            {
                                x = Pj.X + Math.Cos(Ax + Math.PI) * Dl;
                                y = Pj.Y + Math.Sin(Ax + Math.PI) * Dl;
                            }

                            else
                            {
                                double Dt = (b - 2 * rect) / (Math.Max(NbcsJ[i], 2) - 1);

                                x = Pj.X + Math.Cos(Ax + Math.PI) * Dl + Math.Cos((Ax + Math.PI) - Math.PI / 2) * (b / 2 - rect) + Math.Cos((Ax + Math.PI) + Math.PI / 2) * j * Dt;
                                y = Pj.Y + Math.Sin(Ax + Math.PI) * Dl + Math.Sin((Ax + Math.PI) - Math.PI / 2) * (b / 2 - rect) + Math.Sin((Ax + Math.PI) + Math.PI / 2) * j * Dt;
                            }


                            BarrasJ.Add(new Rebar(PhisJ[i], new Point3D(x, y, 0)));
                        }
                    }
                }
                else
                {
                    for (int j = i; j < 20; j++)
                    {
                        PhisJ[j] = 0;
                    }
                    break;
                }
            }

            if (NbcsJ.All(n => n == 1))
                GenBarrasMH();

            LJ = Dl;
        }

        public void GenBarrasMV()
        {
            if (nmv > 0 && fmv > 0 && smv > 0)
            {
                BarrasMV = new List<Rebar>();

                double Ax = AxAn / 180 * Math.PI;

                Point P1 = new Point();
                P1.X = Pi.X + Math.Cos(Ax) * (LI);
                P1.Y = Pi.Y + Math.Sin(Ax) * (LI);

                Point P2 = new Point();
                P2.X = Pj.X + Math.Cos((Ax + Math.PI)) * (LJ);
                P2.Y = Pj.Y + Math.Sin((Ax + Math.PI)) * (LJ);

                int nbm = (int)Math.Floor((P2 - P1).Length / (Convert.ToDouble(smv) / 100));

                //Espacio entre armadura de punta y primera barra de MV
                double esp = ((L - LI - LJ) - (nbm - 1) * (smv / 100.0)) / 2;
                while (Math.Round(esp, 3) < Math.Min((smv / 100.0), 0.15))
                {
                    nbm = nbm - 1;
                    esp = ((L - LI - LJ) - (nbm - 1) * (smv / 100.0)) / 2;
                }

                double Lo = ((P2 - P1).Length - (nbm - 1) * (Convert.ToDouble(smv) / 100)) / 2;

                double rect = 0.035;
                double Dt = (b - 2 * rect) / (Math.Max(nmv, 2) - 1);

                for (int i = 0; i < nbm; i++)
                {
                    if (nmv == 1)
                    {
                        double x = P1.X + Math.Cos(Ax) * Lo + Math.Cos(Ax - Math.PI / 2) * (b / 2 - rect) +
                            Math.Cos(Ax) * i * (Convert.ToDouble(smv) / 100) + Math.Cos(Ax + Math.PI / 2) * 0.5 * (b - 2 * rect);
                        double y = P1.Y + Math.Sin(Ax) * Lo + Math.Sin(Ax - Math.PI / 2) * (b / 2 - rect) +
                            Math.Sin(Ax) * i * (Convert.ToDouble(smv) / 100) + Math.Sin(Ax + Math.PI / 2) * 0.5 * (b - 2 * rect);

                        BarrasMV.Add(new Rebar(fmv, new Point3D(x, y, 0)));
                    }
                    else
                    {
                        for (int j = 0; j < nmv; j++)
                        {
                            double x = P1.X + Math.Cos(Ax) * Lo + Math.Cos(Ax - Math.PI / 2) * (b / 2 - rect) +
                                Math.Cos(Ax) * i * (Convert.ToDouble(smv) / 100) + Math.Cos(Ax + Math.PI / 2) * j * Dt;
                            double y = P1.Y + Math.Sin(Ax) * Lo + Math.Sin(Ax - Math.PI / 2) * (b / 2 - rect) +
                                Math.Sin(Ax) * i * (Convert.ToDouble(smv) / 100) + Math.Sin(Ax + Math.PI / 2) * j * Dt;

                            BarrasMV.Add(new Rebar(fmv, new Point3D(x, y, 0)));
                        }
                    }
                }
            }
            else
            {
                BarrasMV = new List<Rebar>();
            }

        }

        public void GenBarrasMH()
        {
            if (nmh == 1)
            {
                nmh = 2;
            }

            //Si hay solo una capa de MV y una sola barra de punta (ej: Baumax)
            if (NbcsI.All(n => n == 1) || NbcsJ.All(n => n == 1))
            {
                nmh = 0;
                BarrasMH = new List<Rebar>();
            }

            else
            {
                if (nmh > 0 && fmh > 0 && smh > 0)
                {
                    BarrasMH = new List<Rebar>() { new Rebar(12, new Point3D()) };
                }

                else
                {
                    BarrasMH = new List<Rebar>();
                }
            }
        }

        public void GenConfinamientoPuntaI()
        {
            //Calcular cuantos estribos pueden haber
            List<int> posicionesE = new List<int>();
            List<int> posicionesE_Lc = new List<int>();

            //Definir espaciamiento
            sEI = 10;

            if (NbcsI[0] > 1 && PhisI[0] > 0) //Si en la primera capa no hay al menos dos barras, no se puede confinar la punta
            {
                bool isInLc = false;

                for (int i = 0; i < 20; i++)
                {
                    if (NbcsI[i] > 1 && PhisI[i] > 0)
                    {
                        if (isInLc == false)
                            posicionesE_Lc.Add(i);
                        posicionesE.Add(i);
                    }
                }

                //Estribos
                nCEI = posicionesE_Lc.Count > 1 ? posicionesE_Lc.Last() + 1 : 0;

                //Trabas cortas
                nTcI = nCEI > 2 ? posicionesE_Lc.Count() - 2 : 0;

                //Trabas largas. Forzamos a que la traba larga nazca siempre en la primera capa. No necesariamente debe llegar hasta la misma capa que los estribos, pero no puede pasarse de esa capa.
                //Si la traba larga no es igual de larga que el estribo, se marca un warning
                nTlI = NbcsI[0] > 2 && NbcsI.Take(nCEI).Count(n => n == NbcsI[0]) >= 2 ? NbcsI[0] - 2 : 0;
                for (int i = 1; i < nCEI; i++)
                {
                    if (NbcsI[i] == NbcsI[0])
                        nCTlI = i + 1;
                }

                if (nCEI_User < nCEI) //Si el usuario ingresó un número menor al requerido por confinamiento, se fuerza el nCEI requerido.
                    nCEI_User = nCEI;

                else if (nCEI_User > nCEI) //Si el usuario ingresó un número mayor al requerido por confinamiento, se verifica si se puede usar.
                {
                    int nCEI_max = posicionesE.Count > 1 ? posicionesE.Last() + 1 : 0;

                    if (nCEI_User <= nCEI_max)
                    {
                        nCEI = nCEI_User;
                        nTcI = nCEI > 2 ? posicionesE.Count(i => i < nCEI) - 2 : 0;
                        nTlI = NbcsI[0] > 2 && NbcsI.Take(nCEI).Count(n => n == NbcsI[0]) >= 2 ? NbcsI[0] - 2 : 0;
                        for (int i = 1; i < nCEI; i++)
                        {
                            if (NbcsI[i] == NbcsI[0])
                                nCTlI = i + 1;
                        }
                    }
                    else
                    {
                        nCEI_User = nCEI_max;
                        nCEI = nCEI_max;

                        nTcI = nCEI > 2 ? posicionesE.Count(i => i < nCEI) - 2 : 0;
                        nTlI = NbcsI[0] > 2 && NbcsI.Take(nCEI).Count(n => n == NbcsI[0]) >= 2 ? NbcsI[0] - 2 : 0;
                        for (int i = 1; i < nCEI; i++)
                        {
                            if (NbcsI[i] == NbcsI[0])
                                nCTlI = i + 1;
                        }
                    }
                }

                //Calcular longitud de confinamiento
                if (nCEI > 1 && fEI > 0 && sEI > 0)
                    LcI = DxsI[nCEI - 1];
                else
                    LcI = 0;
            }
            else
            {
                nCEI = 0;
                nTcI = 0;
                nTlI = 0;
                nCTlI = 0;
                LcI = 0;
            }

            if (IsSyncIJ)
                CopiarConfinamientoIaJ();
        }

        public void GenConfinamientoPuntaJ()
        {
            //Calcular cuantos estribos pueden haber
            List<int> posicionesE = new List<int>();
            List<int> posicionesE_Lc = new List<int>();

            //Definir espaciamiento
            sEJ = 10;

            if (NbcsJ[0] > 1 && PhisJ[0] > 0) //Si en la primera capa no hay al menos dos barras, no se puede confinar la punta
            {
                bool isInLc = false;

                for (int i = 0; i < 20; i++)
                {
                    if (NbcsJ[i] > 1 && PhisJ[i] > 0)
                    {
                        if (isInLc == false)
                            posicionesE_Lc.Add(i);
                        posicionesE.Add(i);
                    }
                }

                //Estribos
                nCEJ = posicionesE_Lc.Count > 1 ? posicionesE_Lc.Last() + 1 : 0;

                //Trabas cortas
                nTcJ = nCEJ > 2 ? nCEJ - 2 : 0;

                //Trabas largas. Forzamos a que la traba larga nazca siempre en la primera capa
                //nTlJ = NbcsJ[0] > 2 && NbcsJ[nCEJ - 1] == NbcsJ[0] ? NbcsJ[0] - 2 : 0;
                nTlJ = NbcsJ[0] > 2 && NbcsJ.Count(n => n == NbcsJ[0]) >= 2 ? NbcsJ[0] - 2 : 0;
                for (int i = 1; i < nCEJ; i++)
                {
                    if (NbcsJ[i] == NbcsJ[0])
                        nCTlJ = i + 1;
                }

                if (nCEJ_User < nCEJ) //Si el usuario ingresó un número menor al requerido por confinamiento, se fuerza el nCEI requerido.
                    nCEJ_User = nCEJ;

                else if (nCEJ_User > nCEJ) //Si el usuario ingresó un número mayor al requerido por confinamiento, se verifica si se puede usar.
                {
                    int nCEJ_max = posicionesE.Count > 1 ? posicionesE.Last() + 1 : 0;

                    if (nCEJ_User <= nCEJ_max)
                    {
                        nCEJ = nCEJ_User;
                        nTcJ = nCEJ > 2 ? posicionesE.Count(i => i < nCEJ) - 2 : 0;
                        nTlJ = NbcsJ[0] > 2 && NbcsJ[nCEJ - 1] == NbcsJ[0] ? NbcsJ[0] - 2 : 0;
                        for (int i = 1; i < nCEJ; i++)
                        {
                            if (NbcsJ[i] == NbcsJ[0])
                                nCTlJ = i + 1;
                        }
                    }
                    else
                    {
                        nCEJ_User = nCEJ_max;
                        nCEJ = nCEJ_max;

                        nTcJ = nCEJ > 2 ? posicionesE.Count(i => i < nCEJ) - 2 : 0;
                        nTlJ = NbcsJ[0] > 2 && NbcsJ.Take(nCEJ).Count(n => n == NbcsJ[0]) >= 2 ? NbcsJ[0] - 2 : 0;
                        for (int i = 1; i < nCEJ; i++)
                        {
                            if (NbcsJ[i] == NbcsJ[0])
                                nCTlJ = i + 1;
                        }
                    }
                }

                //Calcular longitud de confinamiento
                if (nCEJ > 1 && fEJ > 0 && sEJ > 0)
                    LcJ = DxsJ[nCEJ - 1];
                else
                    LcJ = 0;
            }
            else
            {
                nCEJ = 0;
                nTcJ = 0;
                nTlJ = 0;
                nCTlJ = 0;
                LcJ = 0;
            }
        }

        public void PlotWall(Canvas cv, bool IsDetalle = false, PuntaMuro punta = PuntaMuro.I)
        {
            if (!cv.IsLoaded)
                return;

            cv.Children.Clear();

            List<Point3D> geoml = new List<Point3D>();
            List<Rebar> barrasIl = new List<Rebar>();
            List<Rebar> barrasJl = new List<Rebar>();
            List<Rebar> barrasMVl = new List<Rebar>();
            double Lp = 0;

            if (IsDetalle)
            {
                double Ax = -AxAn / 180 * Math.PI;
                double cos = Math.Cos(Ax);
                double sin = Math.Sin(Ax);

                foreach (Rebar r in BarrasI)
                    barrasIl.Add(new Rebar(r.Phi, new Point3D(r.P1.X * cos - r.P1.Y * sin, r.P1.X * sin + r.P1.Y * cos, 0)));
                foreach (Rebar r in BarrasJ)
                    barrasJl.Add(new Rebar(r.Phi, new Point3D(r.P1.X * cos - r.P1.Y * sin, r.P1.X * sin + r.P1.Y * cos, 0)));
                foreach (Rebar r in BarrasMV)
                    barrasMVl.Add(new Rebar(r.Phi, new Point3D(r.P1.X * cos - r.P1.Y * sin, r.P1.X * sin + r.P1.Y * cos, 0)));
                //Ajustar geoml para que muestre solo la punta del muro

                switch (punta)
                {
                    case PuntaMuro.I:
                        Lp = Math.Min(LI + 0.45, L / 2);
                        geoml.Add(new Point3D(Geom[0].X * cos - Geom[0].Y * sin, Geom[0].X * sin + Geom[0].Y * cos, 0));
                        geoml.Add(new Point3D(geoml.Last().X, geoml.Last().Y + b, 0));
                        geoml.Add(new Point3D(geoml.Last().X + Lp, geoml.Last().Y, 0));
                        geoml.Add(new Point3D(geoml.Last().X, geoml.Last().Y - b, 0));
                        break;

                    case PuntaMuro.J:
                        Lp = Math.Min(LJ + 0.45, L / 2);
                        geoml.Add(new Point3D(Geom[1].X * cos - Geom[1].Y * sin, Geom[1].X * sin + Geom[1].Y * cos, 0));
                        geoml.Add(new Point3D(geoml.Last().X, geoml.Last().Y + b, 0));
                        geoml.Add(new Point3D(geoml.Last().X - Lp, geoml.Last().Y, 0));
                        geoml.Add(new Point3D(geoml.Last().X, geoml.Last().Y - b, 0));
                        break;
                }
            }
            else
            {
                geoml = Geom;
                barrasIl = BarrasI;
                barrasJl = BarrasJ;
                barrasMVl = BarrasMV;
            }

            double xmin = geoml.Min(gm => gm.X);
            double xmax = geoml.Max(gm => gm.X);
            double ymin = geoml.Min(gm => gm.Y);
            double ymax = geoml.Max(gm => gm.Y);

            double dx = xmax - xmin;
            double dy = ymax - ymin;

            double minW = 50;
            double minH = 50;

            double W = (Double.IsNaN(cv.Width)) ? cv.ActualWidth : cv.Width;
            double H = (Double.IsNaN(cv.Height)) ? cv.ActualHeight : cv.Height;

            double margX = IsDetalle ? 20 : 10;
            double margY = IsDetalle ? 0 : 10;

            double ampXi = (Math.Max(W, minW) - 2 * margX) / (dx);
            double ampYi = (Math.Max(H, minH) - 2 * margY) / (dy);

            double amp = Math.Min(ampXi, ampYi);

            double Dx = -xmin * amp + margX + (Math.Max(W, minW) - dx * amp - 2 * margX) / 2 + 0.5 * margX;
            double Dy = -ymin * amp + margY + (Math.Max(H, minH) - dy * amp - 2 * margY) / 2 + 0.5 * margY;

            PointCollection pcl = new PointCollection();
            foreach (Point3D p in geoml.GetRange(0, 4))
                pcl.Add(new Point(p.X * amp + Dx, H - (p.Y * amp + Dy)));

            if (!IsDetalle)
            {
                Polygon poly = new Polygon();
                poly.Stroke = Brushes.Black;
                poly.Fill = Brushes.LightGray;
                poly.StrokeThickness = 0.0015 * amp;
                poly.Points = pcl;
                cv.Children.Add(poly);
                poly.DataContext = this;
            }
            else
            {
                Path path = new Path();
                PathGeometry pathGeom = new PathGeometry();
                PathFigure pathFigure = new PathFigure();

                pathFigure.IsClosed = true;

                Point pc = new Point((pcl[2].X + pcl[3].X) / 2, (pcl[2].Y + pcl[3].Y) / 2);
                double ellCurv = 30;

                pathFigure.StartPoint = pcl[0];
                LineSegment s1 = new LineSegment(pcl[1], true);
                pathFigure.Segments.Add(s1);
                LineSegment s2 = new LineSegment(pcl[2], true);
                pathFigure.Segments.Add(s2);
                ArcSegment s3 = new ArcSegment(pcl[2], new Size(ellCurv, b / 2 * amp), 0, false, SweepDirection.Counterclockwise, true);
                s3.Point = pc;
                pathFigure.Segments.Add(s3);
                ArcSegment s4 = new ArcSegment(pc, new Size(ellCurv, b / 2 * amp), 0, false, SweepDirection.Clockwise, true);
                s4.Point = pcl[3];
                pathFigure.Segments.Add(s4);
                LineSegment s5 = new LineSegment(pcl[0], true);
                pathFigure.Segments.Add(s5);

                pathGeom.Figures.Add(pathFigure);
                path.Data = pathGeom;

                path.Stroke = Brushes.Black;
                path.StrokeThickness = 0.0015 * amp;
                path.Fill = Brushes.DarkGray;

                cv.Children.Add(path);

                //Mascara para ocultar MH
                path = new Path();
                pathGeom = new PathGeometry();
                pathFigure = new PathFigure();

                pathFigure.IsClosed = true;
                pathFigure.StartPoint = pcl[2];

                ArcSegment s6 = new ArcSegment(pcl[2], new Size(ellCurv, b / 2 * amp), 0, false, SweepDirection.Counterclockwise, true);
                s6.Point = pc;
                pathFigure.Segments.Add(s6);
                ArcSegment s7 = new ArcSegment(pc, new Size(ellCurv, b / 2 * amp), 0, false, SweepDirection.Clockwise, true);
                s7.Point = pcl[3];
                pathFigure.Segments.Add(s7);
                LineSegment s8 = new LineSegment(VecSum(pcl[3], ScaleMult((punta == PuntaMuro.I ? L : -L) * amp, new Point(1, 0))), true);
                pathFigure.Segments.Add(s8);
                LineSegment s9 = new LineSegment(VecSum(VecSum(pcl[3], ScaleMult((punta == PuntaMuro.I ? L : -L) * amp, new Point(1, 0))), ScaleMult(b * amp, new Point(0, -1))), true);
                pathFigure.Segments.Add(s9);
                LineSegment s10 = new LineSegment(pcl[2], true);
                pathFigure.Segments.Add(s10);

                pathGeom.Figures.Add(pathFigure);
                path.Data = pathGeom;

                path.Stroke = Brushes.White;
                path.StrokeThickness = 0;
                path.Fill = Brushes.White;

                Canvas.SetZIndex(path, 5);
                cv.Children.Add(path);
            }

            if (!IsDetalle || (IsDetalle && punta == PuntaMuro.I))
            {
                foreach (Rebar br in barrasIl)
                {
                    Ellipse el = new Ellipse();
                    el.Width = br.Phi / 1000.0 * amp;
                    el.Height = br.Phi / 1000.0 * amp;
                    Canvas.SetLeft(el, (br.P1.X * amp + Dx) - el.Width / 2);
                    Canvas.SetTop(el, (H - (br.P1.Y * amp + Dy)) - el.Height / 2);
                    Canvas.SetZIndex(el, 2);
                    el.Fill = Brushes.Black;
                    ToolTip ttp = new ToolTip();
                    ttp.Content = "X: " + br.P1.X + " Y: " + br.P1.Y;
                    el.ToolTip = ttp;
                    cv.Children.Add(el);
                }

                foreach (UIElement uie in GenFiguresConfinamientoPunta(PuntaMuro.I, amp, Dx, Dy, H, IsDetalle))
                {
                    Canvas.SetZIndex(uie, 1);
                    cv.Children.Add(uie);
                }

            }

            if (!IsDetalle || (IsDetalle && punta == PuntaMuro.J))
            {
                foreach (Rebar br in barrasJl)
                {
                    Ellipse el = new Ellipse();
                    el.Width = br.Phi / 1000.0 * amp;
                    el.Height = br.Phi / 1000.0 * amp;
                    Canvas.SetLeft(el, (br.P1.X * amp + Dx) - el.Width / 2);
                    Canvas.SetTop(el, (H - (br.P1.Y * amp + Dy)) - el.Height / 2);
                    Canvas.SetZIndex(el, 2);
                    el.Fill = Brushes.Black;
                    ToolTip ttp = new ToolTip();
                    ttp.Content = "X: " + br.P1.X + " Y: " + br.P1.Y;
                    el.ToolTip = ttp;
                    cv.Children.Add(el);
                }

                foreach (UIElement uie in GenFiguresConfinamientoPunta(PuntaMuro.J, amp, Dx, Dy, H, IsDetalle))
                {
                    Canvas.SetZIndex(uie, 1);
                    cv.Children.Add(uie);
                }
            }

            foreach (Rebar br in barrasMVl)
            {
                Ellipse el = new Ellipse();
                el.Width = br.Phi / 1000.0 * amp;
                el.Height = br.Phi / 1000.0 * amp;
                Canvas.SetLeft(el, (br.P1.X * amp + Dx) - el.Width / 2);
                Canvas.SetTop(el, (H - (br.P1.Y * amp + Dy)) - el.Height / 2);
                Canvas.SetZIndex(el, 0);
                el.Fill = Brushes.Red;
                ToolTip ttp = new ToolTip();
                ttp.Content = "X: " + br.P1.X + " Y: " + br.P1.Y;
                el.ToolTip = ttp;

                if (!IsDetalle
                    || (IsDetalle && punta == PuntaMuro.I && (br.P1.X + br.Phi / 2000.0) <= geoml[2].X)
                    || (IsDetalle && punta == PuntaMuro.J && (br.P1.X - br.Phi / 2000.0) >= geoml[2].X))
                    cv.Children.Add(el);
            }

            //Malla horizontal
            if (BarrasMH.Count > 0)
            {
                if (!IsDetalle)
                {
                    Rebar barA = BarrasI[0];
                    Rebar barB = BarrasI[NbcsI[0] - 1];
                    Rebar barC = BarrasJ[0];
                    Rebar barD = BarrasJ[NbcsJ[0] - 1];
                    double Ax = AxAn * Math.PI / 180;
                    UIElement curvaMH = GenCurvaMH(barA, barB, barC, barD, fmh, amp, Dx, Dy, H, IsDetalle);
                    Canvas.SetZIndex(curvaMH, 0);
                    cv.Children.Add(curvaMH);
                }
                else
                {
                    Rebar barA = BarrasI[0];
                    Rebar barB = BarrasI[NbcsI[0] - 1];
                    Rebar barC = BarrasJ[0];
                    Rebar barD = BarrasJ[NbcsJ[0] - 1];
                    double Ax = AxAn * Math.PI / 180;
                    UIElement curvaMH = GenCurvaMH(barA, barB, barC, barD, fmh, amp, Dx, Dy, H, IsDetalle);
                    Canvas.SetZIndex(curvaMH, 0);
                    cv.Children.Add(curvaMH);
                }
            }
        }

        private List<UIElement> GenFiguresConfinamientoPunta(PuntaMuro punta, double amp, double Dx, double Dy, double H, bool IsDetalle = false)
        {
            List<UIElement> confElements = new List<UIElement>();

            List<Rebar> barras = punta == PuntaMuro.I ? BarrasI : BarrasJ;

            int[] nbsl = punta == PuntaMuro.I ? NbcsI : NbcsJ;
            int nCE = punta == PuntaMuro.I ? nCEI : nCEJ;
            int nTl = punta == PuntaMuro.I ? nTlI : nTlJ;
            int nTc = punta == PuntaMuro.I ? nTcI : nTcJ;
            int sE = punta == PuntaMuro.I ? sEI : sEJ;
            int nCTl = punta == PuntaMuro.I ? nCTlI : nCTlJ;

            if (nCE <= 0 || sE <= 0)
                return confElements;

            #region Estribo
            Rebar barA = barras[0];
            Rebar barB = barras[nbsl[0] - 1];
            Rebar barC = barras[BarrasAntesCapaNCount(punta, nCE) + nbsl[nCE - 1] - 1];
            Rebar barD = barras[BarrasAntesCapaNCount(punta, nCE)];

            UIElement cEstribo = GenCurvaEstribo(barA, barB, barC, barD, punta == PuntaMuro.I ? fEI : fEJ, amp, Dx, Dy, H, IsDetalle);
            confElements.Add(cEstribo);
            #endregion

            #region Traba larga
            if (nTl > 0)
            {
                for (int i = 1; i < nbsl[0] - 1; i++)
                {
                    Rebar barATl = barras[i];
                    Rebar barBTl = barras[BarrasAntesCapaNCount(punta, nCTl) + i];
                    UIElement cTl = GenCurvaTraba(barATl, barBTl, punta == PuntaMuro.I ? fTlI : fTlJ, amp, Dx, Dy, H, IsDetalle);
                    confElements.Add(cTl);
                }
            }
            #endregion

            #region Trabas cortas
            if (nTc > 0)
            {
                for (int i = 1; i < nCE - 1; i++)
                {
                    if (nbsl[i] > 1)
                    {
                        Rebar barATc = barras[BarrasAntesCapaNCount(punta, i + 1)];
                        Rebar barBTc = barras[BarrasAntesCapaNCount(punta, i + 1) + nbsl[i] - 1];
                        UIElement cTc = GenCurvaTraba(barATc, barBTc, punta == PuntaMuro.I ? fTcI : fTcJ, amp, Dx, Dy, H, IsDetalle);
                        confElements.Add(cTc);
                    }
                }
            }
            #endregion

            return confElements;
        }

        private int BarrasAntesCapaNCount(PuntaMuro punta, int N)
        {
            if (N < 2)
                return 0;

            int count = 0;
            for (int i = 0; i < (N - 1); i++)
                count = count + (punta == PuntaMuro.I ? NbcsI[i] : NbcsJ[i]);

            return count;
        }

        private UIElement GenCurvaEstribo(Rebar barA, Rebar barB, Rebar barC, Rebar barD, double phi, double amp, double Dx, double Dy, double H, bool IsDetalle)
        {
            Point pA = new Point(barA.P1.X, barA.P1.Y);
            Point pB = new Point(barB.P1.X, barB.P1.Y);
            Point pC = new Point(barC.P1.X, barC.P1.Y);
            Point pD = new Point(barD.P1.X, barD.P1.Y);

            if (IsDetalle)
            {
                double rot = -AxAn / 180 * Math.PI;

                double sin = Math.Sin(rot);
                double cos = Math.Cos(rot);

                pA = new Point(pA.X * cos - pA.Y * sin, pA.X * sin + pA.Y * cos);
                pB = new Point(pB.X * cos - pB.Y * sin, pB.X * sin + pB.Y * cos);
                pC = new Point(pC.X * cos - pC.Y * sin, pC.X * sin + pC.Y * cos);
                pD = new Point(pD.X * cos - pD.Y * sin, pD.X * sin + pD.Y * cos);
            }

            double dl = Math.Sqrt(Math.Pow(pB.X - pA.X, 2) + Math.Pow(pB.Y - pA.Y, 2));
            Point d1 = new Point((pB.X - pA.X) / dl, (pB.Y - pA.Y) / dl); //Direccion principal del estribo
            Point d2 = new Point(d1.Y, -d1.X); //Direccion perpendicular a la direccion principal del estribo
            double r = (new List<double>() { barA.Phi, barB.Phi, barC.Phi, barD.Phi }.Max() + phi) / 2000.0;

            List<Point> pts = new List<Point>();
            pts.Add(VecSum(pA, ScaleMult(r, Rot(Math.PI / 4.0, ScaleMult(-1, d1)))));
            pts.Add(VecSum(pts.Last(), VecSum(ScaleMult(Math.Sqrt(2) / 2 * LargoPata, d1), ScaleMult(Math.Sqrt(2) / 2 * LargoPata, d2))));
            pts.Add(VecSum(pA, ScaleMult(-r, d2)));
            pts.Add(VecSum(pB, ScaleMult(-r, d2)));
            pts.Add(VecSum(pB, ScaleMult(r, d1)));
            pts.Add(VecSum(pC, ScaleMult(r, d1)));
            pts.Add(VecSum(pC, ScaleMult(r, d2)));
            pts.Add(VecSum(pD, ScaleMult(r, d2)));
            pts.Add(VecSum(pD, ScaleMult(-r, d1)));
            pts.Add(VecSum(pA, ScaleMult(-r, d1)));
            pts.Add(VecSum(pA, ScaleMult(r, Rot(-Math.PI / 4.0, ScaleMult(-1, d2)))));
            pts.Add(VecSum(pts.Last(), VecSum(ScaleMult(Math.Sqrt(2) / 2 * LargoPata, d1), ScaleMult(Math.Sqrt(2) / 2 * LargoPata, d2))));

            List<Point> points = new List<Point>();
            foreach (Point p in pts)
                points.Add(new Point(p.X * amp + Dx, H - (p.Y * amp + Dy)));

            Path ptT = new Path();
            PathGeometry fpgT = new PathGeometry();
            PathFigure pfT = new PathFigure();

            pfT.IsClosed = false;
            pfT.StartPoint = points[1];
            LineSegment s0 = new LineSegment(points[0], true);
            pfT.Segments.Add(s0);
            ArcSegment s1 = new ArcSegment(points[1], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s1.Point = points[2];
            pfT.Segments.Add(s1);
            LineSegment s2 = new LineSegment(points[3], true);
            pfT.Segments.Add(s2);
            ArcSegment s3 = new ArcSegment(points[3], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s3.Point = points[4];
            pfT.Segments.Add(s3);
            LineSegment s4 = new LineSegment(points[5], true);
            pfT.Segments.Add(s4);
            ArcSegment s5 = new ArcSegment(points[5], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s5.Point = points[6];
            pfT.Segments.Add(s5);
            LineSegment s6 = new LineSegment(points[7], true);
            pfT.Segments.Add(s6);
            ArcSegment s7 = new ArcSegment(points[7], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s7.Point = points[8];
            pfT.Segments.Add(s7);
            LineSegment s8 = new LineSegment(points[9], true);
            pfT.Segments.Add(s8);
            ArcSegment s9 = new ArcSegment(points[9], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s9.Point = points[10];
            pfT.Segments.Add(s9);
            LineSegment s10 = new LineSegment(points[11], true);
            pfT.Segments.Add(s10);

            fpgT.Figures.Add(pfT);
            ptT.Data = fpgT;

            ptT.Stroke = Brushes.DarkGreen;
            ptT.StrokeThickness = (phi / 1000.0) * amp;
            ptT.Fill = Brushes.Transparent;

            return ptT;
        }

        private UIElement GenCurvaTraba(Rebar barA, Rebar barB, double phi, double amp, double Dx, double Dy, double H, bool IsDetalle)
        {
            Point pA = new Point(barA.P1.X, barA.P1.Y);
            Point pB = new Point(barB.P1.X, barB.P1.Y);

            if (IsDetalle) //Rotar para dejar con AxAn = 0
            {
                double rot = -AxAn / 180 * Math.PI;

                double sin = Math.Sin(rot);
                double cos = Math.Cos(rot);

                pA = new Point(pA.X * cos - pA.Y * sin, pA.X * sin + pA.Y * cos);
                pB = new Point(pB.X * cos - pB.Y * sin, pB.X * sin + pB.Y * cos);
            }

            double dl = Math.Sqrt(Math.Pow(pB.X - pA.X, 2) + Math.Pow(pB.Y - pA.Y, 2));
            Point d1 = new Point((pB.X - pA.X) / dl, (pB.Y - pA.Y) / dl); //Direccion principal de la traba
            Point d2 = new Point(d1.Y, -d1.X); //Direccion perpendicular a la direccion principal de la traba
            double r = Math.Max((barA.Phi + phi) / 2000.0, (barB.Phi + phi) / 2000.0);

            List<Point> pts = new List<Point>();

            pts.Add(new Point(pA.X - d2.X * r, pA.Y - d2.Y * r));
            pts.Add(new Point(pB.X - d2.X * r, pB.Y - d2.Y * r));
            pts.Add(new Point(pB.X + (Math.Sqrt(2) / 2) * (d1.X + d1.Y) * r, pB.Y + (Math.Sqrt(2) / 2) * (-d1.X + d1.Y) * r));
            pts.Add(new Point(pts.Last().X + (Math.Sqrt(2) / 2) * (d2.X - d1.X) * LargoPata, pts.Last().Y + (Math.Sqrt(2) / 2) * (d2.Y - d1.Y) * LargoPata));
            pts.Add(new Point(pA.X + (Math.Sqrt(2) / 2) * (-d1.X + d1.Y) * r, pA.Y + (Math.Sqrt(2) / 2) * (-d1.X - d1.Y) * r));
            pts.Add(new Point(pts.Last().X + (Math.Sqrt(2) / 2) * (d2.X + d1.X) * LargoPata, pts.Last().Y + (Math.Sqrt(2) / 2) * (d2.Y + d1.Y) * LargoPata));

            List<Point> points = new List<Point>();
            foreach (Point p in pts)
                points.Add(new Point(p.X * amp + Dx, H - (p.Y * amp + Dy)));

            Path ptT = new Path();
            PathGeometry fpgT = new PathGeometry();
            PathFigure pfT = new PathFigure();

            pfT.IsClosed = false;
            pfT.StartPoint = points[5];
            LineSegment sA1 = new LineSegment(points[4], true);
            pfT.Segments.Add(sA1);
            ArcSegment sA2 = new ArcSegment(points[4], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            sA2.Point = points[0];
            pfT.Segments.Add(sA2);
            LineSegment sA3 = new LineSegment(points[1], true);
            pfT.Segments.Add(sA3);
            ArcSegment sA4 = new ArcSegment(points[1], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            sA4.Point = points[2];
            pfT.Segments.Add(sA4);
            LineSegment sA5 = new LineSegment(points[3], true);
            pfT.Segments.Add(sA5);

            fpgT.Figures.Add(pfT);
            ptT.Data = fpgT;

            ptT.Stroke = Brushes.DarkBlue;
            ptT.StrokeThickness = (phi / 1000.0) * amp;
            ptT.Fill = Brushes.Transparent;

            return ptT;
        }

        private UIElement GenCurvaMH(Rebar barA, Rebar barB, Rebar barC, Rebar barD, double phi, double amp, double Dx, double Dy, double H, bool IsDetalle)
        {
            Point pA = new Point(barA.P1.X, barA.P1.Y);
            Point pB = new Point(barB.P1.X, barB.P1.Y);
            Point pC = new Point(barC.P1.X, barC.P1.Y);
            Point pD = new Point(barD.P1.X, barD.P1.Y);

            if (IsDetalle)
            {
                double rot = -AxAn / 180 * Math.PI;

                double sin = Math.Sin(rot);
                double cos = Math.Cos(rot);

                pA = new Point(pA.X * cos - pA.Y * sin, pA.X * sin + pA.Y * cos);
                pB = new Point(pB.X * cos - pB.Y * sin, pB.X * sin + pB.Y * cos);
                pC = new Point(pC.X * cos - pC.Y * sin, pC.X * sin + pC.Y * cos);
                pD = new Point(pD.X * cos - pD.Y * sin, pD.X * sin + pD.Y * cos);
            }

            double dl = Math.Sqrt(Math.Pow(pB.X - pA.X, 2) + Math.Pow(pB.Y - pA.Y, 2));
            Point d1 = new Point((pB.X - pA.X) / dl, (pB.Y - pA.Y) / dl); //Direccion principal del estribo
            Point d2 = new Point(d1.Y, -d1.X); //Direccion perpendicular a la direccion principal del estribo
            double r = (new List<double>() { barA.Phi, barB.Phi, barC.Phi, barD.Phi }.Max() + phi) / 2000.0;

            List<Point> pts = new List<Point>();
            pts.Add(VecSum(pA, ScaleMult(-r, d1)));
            pts.Add(VecSum(pA, ScaleMult(-r, d2)));
            pts.Add(VecSum(pB, ScaleMult(-r, d2)));
            pts.Add(VecSum(pB, ScaleMult(r, d1)));
            pts.Add(VecSum(pC, ScaleMult(r, d1)));
            pts.Add(VecSum(pC, ScaleMult(r, d2)));
            pts.Add(VecSum(pD, ScaleMult(r, d2)));
            pts.Add(VecSum(pD, ScaleMult(-r, d1)));

            List<Point> points = new List<Point>();
            foreach (Point p in pts)
                points.Add(new Point(p.X * amp + Dx, H - (p.Y * amp + Dy)));

            Path ptT = new Path();
            PathGeometry fpgT = new PathGeometry();
            PathFigure pfT = new PathFigure();

            pfT.IsClosed = true;
            pfT.StartPoint = points[0];
            ArcSegment s1 = new ArcSegment(points[0], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s1.Point = points[1];
            pfT.Segments.Add(s1);
            LineSegment s2 = new LineSegment(points[2], true);
            pfT.Segments.Add(s2);
            ArcSegment s3 = new ArcSegment(points[2], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s3.Point = points[3];
            pfT.Segments.Add(s3);
            LineSegment s4 = new LineSegment(points[4], true);
            pfT.Segments.Add(s4);
            ArcSegment s5 = new ArcSegment(points[4], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s5.Point = points[5];
            pfT.Segments.Add(s5);
            LineSegment s6 = new LineSegment(points[6], true);
            pfT.Segments.Add(s6);
            ArcSegment s7 = new ArcSegment(points[6], new Size(r * amp, r * amp), 0, false, SweepDirection.Clockwise, true);
            s7.Point = points[7];
            pfT.Segments.Add(s7);
            LineSegment s8 = new LineSegment(points[0], true);
            pfT.Segments.Add(s8);

            fpgT.Figures.Add(pfT);
            ptT.Data = fpgT;

            ptT.Stroke = Brushes.LightCoral;
            ptT.StrokeThickness = (phi / 1000.0) * amp;
            ptT.Fill = Brushes.Transparent;

            return ptT;
        }

        private void CopiarArmaduraPuntaIaJ()
        {
            PhisJ = (int[])PhisI.Clone();
            NbcJ = NbcI;
            NbcsJ = (int[])NbcsI.Clone();
            DlsJ = (double[])DlsI.Clone();

            GenBarrasPuntaJ();
        }

        private void CopiarConfinamientoIaJ()
        {
            fEJ = fEI;
            fTcJ = fTcI;
            fTlJ = fTlI;
            sEJ = sEI;
            nCEJ = nCEI;
            nCEJ_User = nCEI;
            nTcJ = nTcI;
            nTlJ = nTlI;
            LcJ = LcI;
            nCTlJ = nCTlI;
        }

        public static Point VecSum(Point v1, Point v2)
        {
            return new Point(v1.X + v2.X, v1.Y + v2.Y);
        }

        public static Point ScaleMult(double s, Point v)
        {
            return new Point(s * v.X, s * v.Y);
        }

        public static Point Rot(double theta, Point v)
        {
            double cos = Math.Cos(theta);
            double sin = Math.Sin(theta);

            return new Point(v.X * cos - v.Y * sin, v.X * sin + v.Y * cos);
        }
    }

    public class Rebar
    {
        public int Phi { get; set; }
        public Point3D P1 { get; set; }

        public Rebar(int Phi, Point3D P1)
        {
            this.Phi = Phi;
            this.P1 = P1;
        }
    }
}