Exemple #1
0
 private static IEnumerable <VertexInfo[]> Pawn()
 {
     return(CreateMesh(false, true, DecodeSvgPath.Do(@"M 13,0 C 19,0 22,3 22,9 22,15 19,14 19,20 19,28 26,30 26,48 26,58 20,57 13,57", .1)
                       .FirstOrDefault()
                       .SelectConsecutivePairs(true, (p1, p2) => p1 == p2 ? (PointD?)null : p1)
                       .Where(p => p != null)
                       .Select(p => p.Value + new PointD(-13, -57))
                       .Select(p => Enumerable.Range(0, 72).Select(i => i * 360 / 72).Select(angle => pt(p.X * cos(angle), -p.Y, p.X * sin(angle))).ToArray())
                       .Reverse()
                       .ToArray()));
 }
Exemple #2
0
        private static IEnumerable<VertexInfo[]> AmPm(string text)
        {
            var faces = DecodeSvgPath.Do(Utils.FontToSvgPath(text, "Proxima Nova ExCn Rg", 1), .01).Triangulate();
            var minX = faces.Min(f => f.Min(p => p.X));
            var maxX = faces.Max(f => f.Max(p => p.X));
            var minY = faces.Min(f => f.Min(p => p.Y));
            var maxY = faces.Max(f => f.Max(p => p.Y));
            var cx = (minX + maxX) / 2;
            var cy = (minY + maxY) / 2;

            return faces.Select(parr => parr.Select(p => pt(p.X - cx, 0, p.Y - cy).WithNormal(0, 1, 0)).Reverse().ToArray());
        }
Exemple #3
0
        private static void DoGallows()
        {
            IEnumerable <PointD> decode(string str) => DecodeSvgPath.DecodePieces(str).SelectMany(ps => (ps.Points ?? new PointD[0]));

            var data = decode(@"M 40,822.375 40,872.375 70,872.375 70,852.375 87.5,852.375 140,922.375 140,1002.375 100,1002.375 100,1032.375 210,1032.375 210,1002.375 170,1002.375 170,822.375 z M 125,852.375 140,852.375 140,872.375 z")
                       .Concat(decode(@"M 45,827.375 45,867.375 65,867.375 65,847.375 90,847.375 145,920.71875 145,1007.375 105,1007.375 105,1027.375 205,1027.375 205,1007.375 165,1007.375 165,827.375 z M 115,847.375 145,847.375 145,887.375 z"))
                       .Select(p => new PointD(p.X, p.Y - 802.36218))
                       .ToArray();

            GraphicsUtil.DrawBitmap(500, 500, g =>
            {
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                g.Clear(Color.White);
                using (var tr = new GraphicsTransformer(g).Scale(2, 2))
                {
                    for (int i = 0; i < data.Length; i++)
                    {
                        if (!new[] { 31, 15, 12, 28 }.Contains(i))
                        {
                            g.DrawLine(new Pen(Color.CornflowerBlue, .5f), data[i].ToPointF(), data[(i + 1) % data.Length].ToPointF());
                        }
                    }
                    for (int i = 0; i < data.Length; i++)
                    {
                        g.DrawString(((char)(i < 10 ? '0' + i : 'A' + i - 10)).ToString(), new Font("Niagara Solid", 10f, FontStyle.Bold), Brushes.Black, (float)data[i].X, (float)data[i].Y, new StringFormat {
                            LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center
                        });
                    }
                }
            }).Save(@"D:\temp\temp.png");

            void mkFile(string file, string objName, string code)
            {
                File.WriteAllText(file, GenerateObjFile(code.Split(',').Select(str => str.Select(ch => ch <= '9' ? ch - '0' : ch - 'A' + 10).Select(ix => pt(data[ix].X, ix >= 16 ? 5 : 0, data[ix].Y)).Reverse().ToArray()), objName, AutoNormal.Flat));
            }

            mkFile(@"D:\c\KTANE\Mafia\Assets\Models\GallowsFront.obj", "GallowsFront", @"GSUJ,GJIH,KTVL,USRM,NQPO");
            mkFile(@"D:\c\KTANE\Mafia\Assets\Models\GallowsBevel.obj", "GallowsBevel", @"0CSG,CBRS,BAQR,QA9P,OP98,7NO8,76MN,5LM6,4KL5,JK43,J32I,HI21,0GH1,TUED,EUVF,DFVT");
        }
Exemple #4
0
        private static IEnumerable <Pt[]> ShipPart(string name)
        {
            const double depth1 = 3;
            const double depth2 = 3.5;

            var xml      = XDocument.Parse(File.ReadAllText($@"D:\c\KTANE\Battleship\Assets\Misc\{name}.svg"));
            var pathElem = xml.Root.Elements().FirstOrDefault(el => el.Name.LocalName == "path");
            var path     = pathElem.Attributes().FirstOrDefault(attr => attr.Name.LocalName == "d").Value;

            foreach (var polyOrig in DecodeSvgPath.Do(path, .25))
            {
                var poly = polyOrig.Select(p => (p - new PointD(50, 50)) / 50);
                foreach (var piece in poly.SelectConsecutivePairs(true, (p1, p2) => new[] { pt(p1.X, depth1, p1.Y), pt(p2.X, depth1, p2.Y), pt(p2.X, depth2, p2.Y), pt(p1.X, depth2, p1.Y) }))
                {
                    yield return(piece.Reverse().ToArray());
                }
                foreach (var tri in new[] { poly }.Triangulate())
                {
                    yield return(tri.Select(p => pt(p.X, depth2, p.Y)).Reverse().ToArray());
                }
            }
        }
        private static IEnumerable <IEnumerable <Pt[]> > getMeshImpl(XElement svg, bool half, double smoothness)
        {
            foreach (var path in svg.Elements().Where(p => p.Name.LocalName == "path").Where(p => (p.Attribute("class")?.Value == "half") == half))
            {
                var y = half ? 5 : 10;
                foreach (var points in DecodeSvgPath.Do(path.Attribute("d").Value, smoothness))
                {
                    var pointsArr = points.ToArray();

                    if (new PolygonD(pointsArr).Area() > 0)
                    {
                        Debugger.Break();
                    }

                    // Walls
                    yield return(pointsArr.SelectConsecutivePairs(true, (p1, p2) => p1 == p2 ? null : new[] { pt(p1.X, 0, p1.Y), pt(p2.X, 0, p2.Y), pt(p2.X, y, p2.Y), pt(p1.X, y, p1.Y) }).Where(arr => arr != null));

                    // Face
                    yield return(pointsArr.Triangulate().Select(ps => ps.Select(p => pt(p.X, y, p.Y)).ToArray()));
                }
            }
        }
Exemple #6
0
        private static IEnumerable <Pt[]> WaterHighlight()
        {
            var svg = @"M 35.8125,98.90625 35.84375,98.90625 C 48.276341,98.911781 57.763822,101.25244 74.3125,108.3125 80.957475,111.04867 89.78287,114.04254 92.53125,114.5625 L 92.59375,114.59375 92.65625,114.59375 C 103.18072,116.88168 119.73587,115.86712 127.875,112.75 L 127.875,112.78125 C 128.37066,112.55943 128.9303,112.31882 129.8125,112.25 130.83085,112.17055 132.48449,112.60948 133.46875,113.59375 135.43729,115.56228 135,116.9568 135,118.1875 135,119.62514 134.45953,121.42222 133.375,122.6875 132.29047,123.95278 130.94852,124.67389 129.5,125.25 126.60297,126.40223 122.87724,127.1063 117.09375,127.96875 L 117.0625,127.96875 117.03125,127.96875 C 108.7355,129.09533 103.5412,129.10051 94.65625,127.875 L 94.625,127.84375 94.5625,127.84375 C 87.692265,126.75324 79.271176,123.92143 63.375,117.28125 L 63.34375,117.28125 C 46.166654,110.08415 30.249317,109.19228 14.125,114.3125 12.594269,114.79988 10.330915,114.61182 8.875,113.375 7.4170859,112.13648 7.0007834,110.51688 6.96875,109.1875 6.9046832,106.52872 8.2155097,103.97793 10.9375,102.5625 L 10.96875,102.5625 10.96875,102.53125 C 13.708506,101.12223 17.179691,100.4304 21.53125,99.8125 25.869968,99.196428 30.898434,98.856414 35.8125,98.90625 z M 32.625,70.25 C 33.387682,70.24167 34.156065,70.24139 34.9375,70.25 35.830568,70.25984 36.71054,70.280072 37.625,70.3125 L 37.65625,70.3125 37.6875,70.3125 C 50.812019,70.922943 57.613492,72.564147 73.53125,79.3125 73.541395,79.316801 73.552347,79.308195 73.5625,79.3125 79.914899,81.949746 88.404393,84.978179 91.53125,85.71875 L 91.5625,85.71875 C 99.439321,87.665829 114.73703,87.63153 121.75,85.75 L 121.78125,85.71875 121.8125,85.71875 C 125.88904,84.661868 127.55305,83.923525 130.3125,84.15625 131.00236,84.214431 131.92067,84.369635 132.84375,85 133.76683,85.630365 134.44101,86.768141 134.6875,87.625 135.18049,89.338717 134.86202,90.084421 134.75,90.8125 134.48128,92.538171 132.93173,94.931067 131.1875,95.875 129.38133,96.852459 127.51067,97.297075 124.5,98.0625 L 124.4375,98.09375 124.40625,98.09375 C 114.90709,100.32885 100.00098,100.41783 90.25,98.3125 L 90.21875,98.3125 C 86.252297,97.405882 79.649595,95.198797 74.59375,93.09375 74.586775,93.090846 74.569469,93.096654 74.5625,93.09375 57.04579,85.928272 51.418488,84.115301 43.1875,83.0625 35.016781,82.056147 21.71872,83.258534 17.8125,84.75 15.51705,85.646945 13.873283,86.422631 11.1875,85.78125 9.8394881,85.459337 8.2028336,84.217405 7.59375,82.78125 7.0049387,81.392895 7.0906258,80.244995 7.25,79.25 7.2509131,79.243426 7.2490698,79.22531 7.25,79.21875 7.2545589,79.190503 7.2453361,79.184234 7.25,79.15625 7.6967677,76.284597 9.79538,74.799045 11.6875,73.8125 13.604066,72.81321 15.834382,72.134925 18.4375,71.59375 22.423524,70.765077 27.286225,70.30831 32.625,70.25 z M 36,41.40625 C 48.323433,41.451084 60.005646,44.790359 78.65625,52.625 96.073307,60.034466 110.8791,60.83208 128.125,55.5625 L 128.125,55.59375 C 128.6731,55.415367 129.30034,55.231393 130.15625,55.25 131.05863,55.269617 132.3098,55.621246 133.21875,56.40625 135.03666,57.976258 135,59.711806 135,60.90625 135,62.12446 134.69505,63.596187 133.875,64.84375 133.05495,66.091313 131.87855,66.971821 130.6875,67.5625 128.31067,68.741256 125.59128,69.226623 121.53125,69.84375 121.52229,69.845112 121.50897,69.842387 121.5,69.84375 111.2911,71.45998 106.78042,71.588739 99.03125,70.875 L 99,70.875 C 89.403997,69.936261 81.497216,67.637082 68.625,62.0625 L 68.59375,62.0625 C 63.15099,59.675324 55.023518,56.775914 51.25,55.78125 42.42082,53.596711 28.112542,53.517609 19.71875,55.75 L 19.71875,55.78125 C 16.796373,56.550297 14.99496,56.979398 13.1875,57.0625 12.28377,57.104051 11.26421,57.090314 10.0625,56.5 8.8958372,55.926902 7.9576949,54.672899 7.59375,53.75 L 7.5625,53.75 C 7.5563901,53.734386 7.5684974,53.734492 7.5625,53.71875 7.5577678,53.706226 7.5357549,53.668639 7.53125,53.65625 L 7.5625,53.65625 C 7.0852223,52.373723 6.9449279,50.69409 7.5,49.25 8.0678924,47.772556 9.1207505,46.732717 10.1875,46 12.320307,44.535042 14.797616,43.848704 18.0625,43.25 24.6369,42.037373 30.399323,41.385874 36,41.40625 z M 35.0625,13.09375 C 35.073088,13.093667 35.083167,13.09383 35.09375,13.09375 48.308225,12.994196 57.956619,15.243702 73.1875,21.84375 L 73.21875,21.84375 C 87.211604,28.008434 95.674895,29.989847 106.3125,30 L 106.34375,30 C 111.19351,29.901994 117.60777,29.349136 119.0625,29 L 119.125,29 119.1875,29 C 124.73516,27.850484 127.18663,27.17862 129.625,27.15625 130.23459,27.150657 130.90922,27.191323 131.78125,27.5 132.65328,27.808677 133.73112,28.583131 134.28125,29.5 135.38149,31.333738 135,32.292803 135,32.8125 135,34.1625 134.38449,35.969606 133.28125,37.125 132.17801,38.280394 130.94761,38.857335 129.59375,39.34375 126.89256,40.314238 123.33559,40.938767 117.65625,41.84375 117.64347,41.845907 117.63778,41.8416 117.625,41.84375 108.99109,43.296191 101.87535,43.190783 92.78125,41.4375 92.768697,41.435202 92.762547,41.439804 92.75,41.4375 85.308094,40.071259 78.877431,37.79268 63.25,31.28125 45.989199,24.049071 32.13203,23.236124 14.3125,28.4375 13.714932,28.608234 13.166775,28.791024 12.375,28.875 11.583225,28.958976 10.078908,28.98292 8.6875,27.78125 7.2960921,26.57958 7.1182514,25.046297 7.09375,24.25 7.0715777,23.529401 7.1423522,23.030717 7.21875,22.5 7.2267755,22.444249 7.2419143,22.40035 7.25,22.34375 7.4845844,20.670249 8.795709,18.273256 10.53125,17.21875 12.323659,16.129691 14.157333,15.661088 17,14.9375 L 17.03125,14.9375 17.03125,14.90625 C 21.602484,13.763441 28.286026,13.095824 35.0625,13.09375 z";

            return(DecodeSvgPath.Do(svg, 1).Select(poly => poly.Select(pt => (pt - p(71, 71)) / 14.2)).Triangulate().Select(e => e.Select(p => pt(p.X, 0, p.Y)).ToArray()));
        }
Exemple #7
0
        private static IEnumerable<VertexInfo[]> Numbers(string fontFamily, bool arabic, double factor, double[] spacing)
        {
            const double depth = .0001;// .005;
            //const double bevelRadius = .005;
            //const int revSteps = 2;

            const double numberRadius = .775;
            const double bézierSmoothness = .001;

            var numbers = arabic ? "1|2,1,2,3,4,5,6,7,8,9,1|0,1|1".Split(',') : "XII,I,II,III,IV,V,VI,VII,VIII,IX,X,XI".Split(',');
            var stuffs = new List<PathStuff>();

            using (var bmp = new Bitmap(1024, 768, PixelFormat.Format32bppArgb))
                for (int i = 0; i < 12; i++)
                {
                    var angle = i * 360 / 12 - 90;
                    var gp = new GraphicsPath();
                    var x = 0.0;
                    foreach (var piece in spacing == null ? new[] { numbers[i].Replace("|", "") } : numbers[i].Split('|'))
                    {
                        gp.AddString(piece, new FontFamily(fontFamily), 0, .25f, new PointF((float) x, 0), new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
                        if (spacing != null && arabic && (i == 0 || i >= 10))
                            x += (i == 0 || i == 10) ? spacing[0] : spacing[1];
                    }
                    var path = new List<DecodeSvgPath.PathPiece>();
                    for (int j = 0; j < gp.PointCount; j++)
                    {
                        var type =
                            ((PathPointType) gp.PathTypes[j]).HasFlag(PathPointType.Bezier) ? DecodeSvgPath.PathPieceType.Curve :
                            ((PathPointType) gp.PathTypes[j]).HasFlag(PathPointType.Line) ? DecodeSvgPath.PathPieceType.Line : DecodeSvgPath.PathPieceType.Move;
                        if (type == DecodeSvgPath.PathPieceType.Curve)
                        {
                            path.Add(new DecodeSvgPath.PathPiece(DecodeSvgPath.PathPieceType.Curve, gp.PathPoints.Subarray(j, 3).Select(p => new PointD(p)).ToArray()));
                            j += 2;
                        }
                        else
                            path.Add(new DecodeSvgPath.PathPiece(type, gp.PathPoints.Subarray(j, 1).Select(p => new PointD(p)).ToArray()));

                        if (((PathPointType) gp.PathTypes[j]).HasFlag(PathPointType.CloseSubpath))
                            path.Add(DecodeSvgPath.PathPiece.End);
                    }

                    var x1 = path.Where(p => p.Type != DecodeSvgPath.PathPieceType.End).SelectMany(p => p.Points).Min(p => p.X);
                    var x2 = path.Where(p => p.Type != DecodeSvgPath.PathPieceType.End).SelectMany(p => p.Points).Max(p => p.X);
                    var y1 = path.Where(p => p.Type != DecodeSvgPath.PathPieceType.End).SelectMany(p => p.Points).Min(p => p.Y);
                    var y2 = path.Where(p => p.Type != DecodeSvgPath.PathPieceType.End).SelectMany(p => p.Points).Max(p => p.Y);
                    var w = x2 - x1;
                    var h = y2 - y1;
                    stuffs.Add(new PathStuff { Path = path, X = x1, Y = y1, W = w, H = h });
                }

            for (int i = 0; i < 12; i++)
            {
                var angle = i * 360 / 12 - 90;
                var path = stuffs[i].Path.Select(pth => pth.Select(p => new PointD((p.X - stuffs[i].X - stuffs[i].W / 2) * factor + numberRadius * cos(angle), (p.Y - stuffs[i].Y - stuffs[i].H / 2) * factor + numberRadius * sin(angle)))).ToList();
                var furthest = path.Where(p => p.Type != DecodeSvgPath.PathPieceType.End).SelectMany(p => p.Points).Max(p => p.Distance());
                var newRadius = numberRadius - (furthest - numberRadius);
                path = stuffs[i].Path.Select(pth => pth.Select(p => new PointD((p.X - stuffs[i].X - stuffs[i].W / 2) * factor + newRadius * cos(angle), (p.Y - stuffs[i].Y - stuffs[i].H / 2) * factor + newRadius * sin(angle)))).ToList();

                //var reverse = false;
                var stuff = DecodeSvgPath.Do(path, bézierSmoothness);
                PointD[][] ts;
                try { ts = stuff.Triangulate().ToArray(); }
                catch (InvalidOperationException)
                {
                    ts = stuff.Select(st => st.Reverse()).Triangulate().ToArray();
                    //reverse = true;
                }
                foreach (var t in ts)
                    yield return t.Select(p => pt(-p.X, depth, -p.Y).WithNormal(0, 1, 0)).Reverse().ToArray();

                //foreach (var c in stuff)
                //    foreach (var b in BevelFromCurve((reverse ? c.Reverse() : c).Select(p => pt(-p.X, depth, -p.Y)), bevelRadius, revSteps))
                //        yield return b;
            }
        }
Exemple #8
0
        public static void DoModels()
        {
            foreach (var inf in new[] { SvgInfo.SingleArrow, SvgInfo.DoubleArrow, SvgInfo.Submit })
            {
                File.WriteAllText($@"D:\c\KTANE\DoubleOh\Assets\Models\Button{inf.Name}.obj", GenerateObjFile(Button(inf.Svg), $"Button{inf.Name}"));
            }
            File.WriteAllText(@"D:\c\KTANE\DoubleOh\Assets\Models\ButtonTop.obj", GenerateObjFile(Button(null), "ButtonTop"));
            File.WriteAllText(@"D:\c\KTANE\DoubleOh\Assets\Models\ButtonHighlight.obj", GenerateObjFile(Button("Highlight"), "ButtonHighlight"));
            File.WriteAllText(@"D:\c\KTANE\DoubleOh\Assets\Models\ButtonCollider.obj", GenerateObjFile(Box().Select(face => face.Select(p => new Pt(p.X * .85, p.Y * .65, p.Z * .85).RotateY(45)).ToArray()), "ButtonCollider"));
            File.WriteAllText(@"D:\c\KTANE\DoubleOh\Assets\Models\Frame.obj", GenerateObjFile(Frame(), "Frame"));

            //var g = XDocument.Parse(File.ReadAllText(@"D:\c\KTANE\DoubleOh\Assets\Sources\Segments.svg")).Root.Elements().FirstOrDefault(e => e.Name.LocalName == "g");
            //foreach (var path in g.Elements().Where(e => e.Name.LocalName == "path"))
            //{
            //    // -982.36218
            //    var svg = DecodeSvgPath.DecodePieces(path.Attributes().FirstOrDefault(a => a.Name.LocalName == "d").Value).Select(piece => piece.Select(p => p + new PointD(0, -982.36218)));
            //    Console.WriteLine(svg.JoinString(" "));
            //}

            var segments = Ut.NewArray(
                @"M2.5, 3.1875 0.5, 5.1875 0.5, 29.78122 5, 34.28122 9.5, 29.78122 9.5, 10.18746 z",
                @"M37.5, 3.1875 30.5, 10.18746 30.5, 29.78122 35, 34.28122 39.5, 29.78122 39.5, 5.1875 z",
                @"M5.1875, 0.5 3.1875, 2.5 10.21875, 9.5 29.78125, 9.5 36.8125, 2.5 34.8125, 0.5 z",
                @"M5, 35.68752 0.5, 40.18752 0.5, 64.78122 2.5, 66.78122 9.5, 59.78122 9.5, 40.18752 z",
                @"M35, 35.68752 30.5, 40.18752 30.5, 59.78122 37.5, 66.78122 39.5, 64.78122 39.5, 40.18752 z",
                @"M10.1875, 30.5 5.6875, 35 10.21875, 39.5 29.78125, 39.5 34.3125, 35 29.8125, 30.5 z",
                @"M10.1875, 60.5 3.1875, 67.5 5.21875, 69.5 34.78125, 69.5 36.8125, 67.5 29.8125, 60.5 z"
                );

            for (int i = 0; i < segments.Length; i++)
            {
                File.WriteAllText($@"D:\c\KTANE\DoubleOh\Assets\Models\Segment{i}.obj", GenerateObjFile(DecodeSvgPath.Do(segments[i], 1).Triangulate().Select(arr => arr.Select(p => pt(p.X, 0, p.Y).WithNormal(0, 1, 0)).Reverse().ToArray()), $"Segment{i}"));
            }
        }
Exemple #9
0
        public static IEnumerable <VertexInfo[]> Button(string svg)
        {
            const double radius1 = .6;
            const double radius2 = .8;
            const double radius3 = .9;
            const double radius4 = 1.0;

            const double h  = 0.6;
            const double y1 = h;
            const double y2 = h;
            const double y3 = h / 2;
            const double y4 = 0;

            const double bevelRadius = .05;

            const double cf               = .2;
            const int    bézierSteps      = 16;
            const int    roundSteps       = 3;
            const double bézierSmoothness = .001;

            const double textureFrame = .4;

            var patch = BézierPatch(
                pt(0, y1, radius1), pt(radius1 * cf, y1, radius1), pt(radius1, y1, radius1 * cf), pt(radius1, y1, 0),
                pt(0, y2, radius2), pt(radius2 * cf, y2, radius2), pt(radius2, y2, radius2 * cf), pt(radius2, y2, 0),
                pt(0, y3, radius3), pt(radius3 * cf, y3, radius3), pt(radius3, y3, radius3 * cf), pt(radius3, y3, 0),
                pt(0, y4, radius4), pt(radius4 * cf, y4, radius4), pt(radius4, y4, radius4 * cf), pt(radius4, y4, 0),
                bézierSteps);

            var patch2 = patch.Select((arr, j) => (j / (double)patch.Length).Apply(v =>
                                                                                   arr.Skip(1).Select((vx, i) => (i / (double)arr.Length).Apply(u => new { Vertex = vx, Texture = p((v + (1 - 2 * textureFrame) * (1 - v)) * u + ((1 - v) * textureFrame), (1 - v) * textureFrame) }))
                                                                                   .Concat(arr.Skip(1).Select((vx, i) => (i / (double)arr.Length).Apply(u => new { Vertex = vx.RotateY(-90), Texture = p(1 - (1 - v) * textureFrame, (v + (1 - 2 * textureFrame) * (1 - v)) * u + ((1 - v) * textureFrame)) })))
                                                                                   .Concat(arr.Skip(1).Select((vx, i) => (i / (double)arr.Length).Apply(u => new { Vertex = vx.RotateY(-180), Texture = p(1 - (v + (1 - 2 * textureFrame) * (1 - v)) * u - ((1 - v) * textureFrame), 1 - (1 - v) * textureFrame) })))
                                                                                   .Concat(arr.Skip(1).Select((vx, i) => (i / (double)arr.Length).Apply(u => new { Vertex = vx.RotateY(-270), Texture = p((1 - v) * textureFrame, 1 - (v + (1 - 2 * textureFrame) * (1 - v)) * u - ((1 - v) * textureFrame)) })))
                                                                                   .ToArray()))
                         .ToArray();

            var patch3 = Ut.NewArray(patch2.Length, patch2[0].Length, (i, j) =>
                                     (
                                         i == 0 ? new MeshVertexInfo(patch2[i][j].Vertex, pt(0, 1, 0)) :
                                         i == patch2.Length - 1 ? new MeshVertexInfo(patch2[i][j].Vertex, Normal.Mine, Normal.Mine, Normal.Average, Normal.Average) :
                                         new MeshVertexInfo(patch2[i][j].Vertex, Normal.Average, Normal.Average, Normal.Average, Normal.Average)
                                     )
                                     .WithTexture(patch2[i][j].Texture));

            var outlineRaw = patch2[0].Select(pt => new { Point = p(pt.Vertex.X, pt.Vertex.Z), Texture = pt.Texture }).ToArray();

            var texturize = Ut.Lambda((PointD pt) =>
            {
                var ix = outlineRaw.IndexOf(inf => inf.Point == pt);
                if (ix != -1)
                {
                    return(outlineRaw[ix].Texture);
                }
                var pp = pt + p(0, radius1);
                return(p(
                           (1 - pp.LengthProjectedOnto(p(-1, 1)) / Math.Sqrt(2) / radius1) * (1 - 2 * textureFrame) + textureFrame,
                           (1 - pp.LengthProjectedOnto(p(1, 1)) / Math.Sqrt(2) / radius1) * (1 - 2 * textureFrame) + textureFrame));
            });

            if (svg == null)
            {
                return(outlineRaw.Select(inf => inf.Point).Triangulate()
                       .Select(f => f.Select(p => pt(p.X, h - bevelRadius, p.Y).WithNormal(0, 1, 0).WithTexture(new PointD(p.X + 1, p.Y + 1) / 2)).ToArray()));
            }

            if (svg == "Highlight")
            {
                const double f = 1.1;
                return(patch2.Last()
                       .Select(p => new { Point1 = pt(p.Vertex.X, y4, p.Vertex.Z), Point2 = pt(p.Vertex.X * f, y1, p.Vertex.Z * f) })
                       .SelectManyConsecutivePairs(true, (i1, i2) => new[] { new[] { i1.Point1, i1.Point2, i2.Point2, i2.Point1 }, new[] { i1.Point2, i1.Point1, i2.Point1, i2.Point2 } })
                       .Select(arr => arr.Select(p => new VertexInfo(p, null)).ToArray()));
            }

            var svgPolygons = DecodeSvgPath.Do(svg, bézierSmoothness).Select(poly => poly.Select(pt => (pt - p(5, 5)) * .11).ToArray()).ToArray();
            var outline     = outlineRaw.Select(inf => inf.Point).ToArray().Concat(svgPolygons).Triangulate()
                              .Select(f => f.Select(p => pt(p.X, patch2[0][0].Vertex.Y, p.Y).WithNormal(0, 1, 0).WithTexture(texturize(p))).Reverse().ToArray());

            return(CreateMesh(false, true, patch3)
                   .Concat(outline)
                   .Concat(svgPolygons.SelectMany(poly => BevelFromCurve(poly.Reverse().Select(p => pt(p.X, h, p.Y)), bevelRadius, roundSteps, Normal.Mine)).Select(face => face.Select(vi => vi.WithTexture(texturize(p(vi.Location.X, vi.Location.Z)))).ToArray())));
        }