예제 #1
0
            public override void Process()
            {
                ResourceManager resourceManager = new ResourceManager(context.Server);

                MapOptions options = MapOptions.SectorGrid | MapOptions.BordersMajor | MapOptions.NamesMajor | MapOptions.NamesMinor;
                Stylesheet.Style style = Stylesheet.Style.Poster;
                ParseOptions(ref options, ref style);

                double x = GetDoubleOption("x", 0);
                double y = GetDoubleOption("y", 0);
                double scale = Util.Clamp(GetDoubleOption("scale", 0), MinScale, MaxScale);
                int width = Util.Clamp(GetIntOption("w", NormalTileWidth), MinDimension, MaxDimension);
                int height = Util.Clamp(GetIntOption("h", NormalTileHeight), MinDimension, MaxDimension);

                Size tileSize = new Size(width, height);

                RectangleF tileRect = new RectangleF();
                tileRect.X = (float)(x * tileSize.Width / (scale * Astrometrics.ParsecScaleX));
                tileRect.Y = (float)(y * tileSize.Height / (scale * Astrometrics.ParsecScaleY));
                tileRect.Width = (float)(tileSize.Width / (scale * Astrometrics.ParsecScaleX));
                tileRect.Height = (float)(tileSize.Height / (scale * Astrometrics.ParsecScaleY));

                DateTime dt = DateTime.Now;
                bool silly = (Math.Abs((int)x % 2) == Math.Abs((int)y % 2)) && (dt.Month == 4 && dt.Day == 1);
                silly = GetBoolOption("silly", silly);

                Selector selector = new RectSelector(SectorMap.ForMilieu(resourceManager, GetStringOption("milieu")), resourceManager, tileRect);
                Stylesheet styles = new Stylesheet(scale, options, style);
                RenderContext ctx = new RenderContext(resourceManager, selector, tileRect, scale, options, styles, tileSize);
                ctx.Silly = silly;
                ctx.ClipOutsectorBorders = true;
                ProduceResponse(context, "Tile", ctx, tileSize);
            }
예제 #2
0
        private static void DrawMicroBorders(RenderContext ctx, BorderLayer layer)
        {
            const byte FILL_ALPHA = 64;

            float[] edgex, edgey;
            PathUtil.PathType borderPathType = ctx.styles.microBorderStyle == MicroBorderStyle.Square ?
                PathUtil.PathType.Square : PathUtil.PathType.Hex;
            RenderUtil.HexEdges(borderPathType, out edgex, out edgey);

            XSolidBrush solidBrush = new XSolidBrush();
            XPen pen = new XPen(XColor.Empty);
            ctx.styles.microBorders.pen.Apply(ref pen);

            foreach (Sector sector in ctx.selector.Sectors)
            {
                XGraphicsPath sectorClipPath = null;

                using (RenderUtil.SaveState(ctx.graphics))
                {
                    // This looks craptacular for Candy style borders :(
                    if (ctx.clipOutsectorBorders &&
                        (layer == BorderLayer.Fill || ctx.styles.microBorderStyle != MicroBorderStyle.Curve))
                    {
                        Sector.ClipPath clip = sector.ComputeClipPath(borderPathType);
                        if (!ctx.tileRect.IntersectsWith(clip.bounds))
                            continue;

                        sectorClipPath = new XGraphicsPath(clip.clipPathPoints, clip.clipPathPointTypes, XFillMode.Alternate);
                        if (sectorClipPath != null)
                            ctx.graphics.IntersectClip(sectorClipPath);
                    }

                    ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;

                    foreach (Border border in sector.Borders)
                    {
                        BorderPath borderPath = border.ComputeGraphicsPath(sector, borderPathType);

                        XGraphicsPath drawPath = borderPath.borderPathPoints.Length > 0 ? new XGraphicsPath(borderPath.borderPathPoints, borderPath.borderPathTypes, XFillMode.Alternate) : null;
                        XGraphicsPath clipPath = new XGraphicsPath(borderPath.clipPathPoints, borderPath.clipPathTypes, XFillMode.Alternate);

                        Color? borderColor = border.Color;
                        LineStyle? borderStyle = border.Style;

                        SectorStylesheet.StyleResult ssr = sector.ApplyStylesheet("border", border.Allegiance);
                        borderStyle = borderStyle ?? ssr.GetEnum<LineStyle>("style") ?? LineStyle.Solid;
                        borderColor = borderColor ?? ssr.GetColor("color") ?? ctx.styles.microBorders.pen.color;

                        if (layer == BorderLayer.Stroke && borderStyle.Value == LineStyle.None)
                            continue;

                        if (ctx.styles.grayscale ||
                            !ColorUtil.NoticeableDifference(borderColor.Value, ctx.styles.backgroundColor))
                        {
                            borderColor = ctx.styles.microBorders.pen.color; // default
                        }

                        pen.Color = borderColor.Value;
                        pen.DashStyle = LineStyleToDashStyle(borderStyle.Value);

                        if (ctx.styles.microBorderStyle != MicroBorderStyle.Curve)
                        {
                            // Clip to the path itself - this means adjacent borders don't clash
                            using (RenderUtil.SaveState(ctx.graphics))
                            {
                                ctx.graphics.IntersectClip(clipPath);
                                if (layer == BorderLayer.Fill)
                                {
                                    solidBrush.Color = Color.FromArgb(FILL_ALPHA, borderColor.Value);
                                    ctx.graphics.DrawPath(solidBrush, clipPath);
                                }

                                if (layer == BorderLayer.Stroke && drawPath != null)
                                    ctx.graphics.DrawPath(pen, drawPath);
                            }
                        }
                        else
                        {
                            if (layer == BorderLayer.Fill)
                            {
                                solidBrush.Color = Color.FromArgb(FILL_ALPHA, borderColor.Value);
                                ctx.graphics.DrawClosedCurve(solidBrush, borderPath.clipPathPoints);
                            }

                            if (layer == BorderLayer.Stroke)
                            {
                                foreach (PointF[] curve in borderPath.curvePoints)
                                {
                                    // TODO: Investigate DrawClosedCurve to handle endings
                                    // Would need to have path computer tell whether
                                    // or not the path was actually a closed loop
                                    // Can do it by clipping borders to sector, but that loses
                                    // bottom/right overlaps
                                    ctx.graphics.DrawCurve(pen, curve, 0.6f);
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #3
0
        private static void DrawLabels(RenderContext ctx, FontCache styleRes)
        {
            using (RenderUtil.SaveState(ctx.graphics))
            {
                XSolidBrush solidBrush = new XSolidBrush();

                ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;

                foreach (Sector sector in ctx.selector.Sectors)
                {
                    solidBrush.Color = ctx.styles.microBorders.textColor;
                    foreach (Border border in sector.Borders.Where(border => border.ShowLabel))
                    {
                        Allegiance alleg = sector.GetAllegianceFromCode(border.Allegiance);
                        if (alleg != null || !String.IsNullOrEmpty(border.Label))
                        {
                            string text = border.Label ?? alleg.Name;
                            Point labelHex = border.LabelPosition;
                            PointF labelPos = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, labelHex)));
                            // TODO: Replace these with, well, positions!
                            //labelPos.X -= 0.5f;
                            //labelPos.Y -= 0.5f;

                            System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"\s+(?![a-z])");
                            if (border.WrapLabel)
                                text = r.Replace(text, "\n");

                            RenderUtil.DrawLabel(ctx.graphics, text, labelPos, ctx.styles.microBorders.Font, solidBrush, ctx.styles.microBorders.textStyle);
                        }
                    }

                    foreach (Label label in sector.Labels)
                    {
                        string text = label.Text;
                        Point labelHex = new Point(label.Hex / 100, label.Hex % 100);
                        PointF labelPos = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, labelHex)));
                        // TODO: Adopt some of the tweaks from .MSEC
                        labelPos.Y -= label.OffsetY * 0.7f;

                        XFont font;
                        switch (label.Size)
                        {
                            case "small": font = ctx.styles.microBorders.SmallFont; break;
                            case "large": font = ctx.styles.microBorders.LargeFont; break;
                            default: font = ctx.styles.microBorders.Font; break;
                        }

                        if (!ctx.styles.grayscale &&
                            ColorUtil.NoticeableDifference(label.Color, ctx.styles.backgroundColor) &&
                            (label.Color != Label.DefaultColor))
                            solidBrush.Color = label.Color;
                        else
                            solidBrush.Color = ctx.styles.microBorders.textColor;
                        RenderUtil.DrawLabel(ctx.graphics, text, labelPos, font, solidBrush, ctx.styles.microBorders.textStyle);
                    }
                }
            }
        }
예제 #4
0
 private static Stylesheet.StyleElement? ZoneStyle(RenderContext ctx, World world)
 {
     if (world.IsAmber || world.IsRed || world.IsBlue)
     {
         Stylesheet.StyleElement elem =
             world.IsAmber ? ctx.styles.amberZone :
             world.IsRed ? ctx.styles.redZone : ctx.styles.blueZone;
         return elem;
     }
     return null;
 }
예제 #5
0
        private static void DrawWorldLabel(RenderContext ctx, TextBackgroundStyle backgroundStyle, XSolidBrush brush, Color color, PointF position, XFont font, string text)
        {
            XSize size = ctx.graphics.MeasureString(text, font);

            switch (backgroundStyle)
            {
                case TextBackgroundStyle.None:
                    break;

                default:
                case TextBackgroundStyle.Rectangle:
                    if (!ctx.styles.fillMicroBorders)
                    {
                        // TODO: Implement this with a clipping region instead
                        brush.Color = ctx.styles.backgroundColor;
                        ctx.graphics.DrawRectangle(brush, position.X - size.Width / 2, position.Y - size.Height / 2, size.Width, size.Height);
                    }
                    break;

                case TextBackgroundStyle.Outline:
                case TextBackgroundStyle.Shadow:
                    {
                        // TODO: These scaling factors are constant for a render; compute once

                        // Invert the current scaling transforms
                        float sx = 1.0f / ctx.styles.hexContentScale;
                        float sy = 1.0f / ctx.styles.hexContentScale;
                        sx *= Astrometrics.ParsecScaleX;
                        sy *= Astrometrics.ParsecScaleY;
                        sx /= (float)ctx.scale * Astrometrics.ParsecScaleX;
                        sy /= (float)ctx.scale * Astrometrics.ParsecScaleY;

                        const int outlineSize = 2;
                        const int outlineSkip = 1;

                        int outlineStart = backgroundStyle == TextBackgroundStyle.Outline
                            ? -outlineSize
                            : 0;

                        brush.Color = ctx.styles.backgroundColor;

                        for (int dx = outlineStart; dx <= outlineSize; dx += outlineSkip)
                        {
                            for (int dy = outlineStart; dy <= outlineSize; dy += outlineSkip)
                            {
                                ctx.graphics.DrawString(text, font, brush, position.X + sx * dx, position.Y + sy * dy, RenderUtil.StringFormatCentered);
                            }
                        }
                        break;
                    }
            }

            brush.Color = color;
            ctx.graphics.DrawString(text, font, brush, position.X, position.Y, RenderUtil.StringFormatCentered);
        }
예제 #6
0
        private static void DrawWorld(RenderContext ctx, FontCache styleRes, World world, WorldLayer layer)
        {
            bool isPlaceholder = world.IsPlaceholder;
            bool isCapital = world.IsCapital;
            bool isHiPop = world.IsHi;
            bool renderName = ctx.styles.worldDetails.HasFlag(WorldDetails.AllNames) ||
                (ctx.styles.worldDetails.HasFlag(WorldDetails.KeyNames) && (isCapital || isHiPop));
            bool renderUWP = ctx.styles.worldDetails.HasFlag(WorldDetails.Uwp);

            using (RenderUtil.SaveState(ctx.graphics))
            {
                XPen pen = new XPen(XColor.Empty);
                XSolidBrush solidBrush = new XSolidBrush();

                ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;

                // Center on the parsec
                PointF center = Astrometrics.HexToCenter(world.Coordinates);

                XMatrix matrix = new XMatrix();
                matrix.TranslatePrepend(center.X, center.Y);
                matrix.ScalePrepend(ctx.styles.hexContentScale / Astrometrics.ParsecScaleX, ctx.styles.hexContentScale / Astrometrics.ParsecScaleY);
                ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend);

                if (!ctx.styles.useWorldImages)
                {
                    if (layer == WorldLayer.Background)
                    {
                        #region Zone
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.Zone))
                        {
                            Stylesheet.StyleElement? maybeElem = ZoneStyle(ctx, world);
                            if (maybeElem.HasValue)
                            {
                                Stylesheet.StyleElement elem = maybeElem.Value;
                                if (!elem.fillColor.IsEmpty)
                                {
                                    solidBrush.Color = elem.fillColor;
                                    ctx.graphics.DrawEllipse(solidBrush, -0.4f, -0.4f, 0.8f, 0.8f);
                                }

                                PenInfo pi = elem.pen;
                                if (!pi.color.IsEmpty)
                                {
                                    pi.Apply(ref pen);

                                    if (renderName && ctx.styles.fillMicroBorders)
                                    {
                                        using (RenderUtil.SaveState(ctx.graphics))
                                        {
                                            ctx.graphics.IntersectClip(new RectangleF(-.5f, -.5f, 1f, renderUWP ? 0.65f : 0.75f));
                                            ctx.graphics.DrawEllipse(pen, -0.4f, -0.4f, 0.8f, 0.8f);
                                        }
                                    }
                                    else
                                    {
                                        ctx.graphics.DrawEllipse(pen, -0.4f, -0.4f, 0.8f, 0.8f);
                                    }
                                }
                            }
                        }
                        #endregion

                        #region Hex
                        if (!ctx.styles.numberAllHexes &&
                            ctx.styles.worldDetails.HasFlag(WorldDetails.Hex))
                        {
                            string hex;
                            switch (ctx.styles.hexCoordinateStyle)
                            {
                                default:
                                case Stylesheet.HexCoordinateStyle.Sector: hex = world.Hex; break;
                                case Stylesheet.HexCoordinateStyle.Subsector: hex = world.SubsectorHex; break;
                            }
                            solidBrush.Color = ctx.styles.hexNumber.textColor;
                            ctx.graphics.DrawString(hex, ctx.styles.hexNumber.Font, solidBrush, 0.0f, -0.5f, RenderUtil.StringFormatTopCenter);
                        }
                        #endregion
                    }

                    if (layer == WorldLayer.Foreground)
                    {
                        Stylesheet.StyleElement? elem = ZoneStyle(ctx, world);
                        TextBackgroundStyle worldTextBackgroundStyle = (elem.HasValue && !elem.Value.fillColor.IsEmpty)
                            ? TextBackgroundStyle.None : ctx.styles.worlds.textBackgroundStyle;

                        #region Name
                        if (renderName)
                        {
                            string name = world.Name;
                            if ((isHiPop && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight)) || ctx.styles.worlds.textStyle.Uppercase)
                                name = name.ToUpperInvariant();

                            Color textColor = (isCapital && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight))
                                ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                            XFont font = ((isHiPop || isCapital) && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight))
                                ? ctx.styles.worlds.LargeFont : ctx.styles.worlds.Font;

                            DrawWorldLabel(ctx, worldTextBackgroundStyle, solidBrush, textColor, ctx.styles.worlds.textStyle.Translation, font, name);
                        }
                        #endregion

                        #region Allegiance
                        // TODO: Mask off background for allegiance
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.Allegiance))
                        {
                            string alleg = world.Allegiance;
                            if (!SecondSurvey.IsDefaultAllegiance(alleg))
                            {
                                if (!ctx.styles.t5AllegianceCodes && alleg.Length > 2)
                                    alleg = SecondSurvey.T5AllegianceCodeToLegacyCode(alleg);

                                solidBrush.Color = ctx.styles.worlds.textColor;

                                if (ctx.styles.lowerCaseAllegiance)
                                    alleg = alleg.ToLowerInvariant();

                                ctx.graphics.DrawString(alleg, ctx.styles.worlds.SmallFont, solidBrush, ctx.styles.AllegiancePosition.X, ctx.styles.AllegiancePosition.Y, RenderUtil.StringFormatCentered);
                            }
                        }
                        #endregion

                        if (!isPlaceholder)
                        {
                            #region GasGiant
                            if (ctx.styles.worldDetails.HasFlag(WorldDetails.GasGiant))
                            {
                                if (world.GasGiants > 0)
                                {
                                    solidBrush.Color = ctx.styles.worlds.textColor;
                                    RenderUtil.DrawGlyph(ctx.graphics, Glyph.Circle, styleRes, solidBrush, ctx.styles.GasGiantPosition.X, ctx.styles.GasGiantPosition.Y);
                                }
                            }
                            #endregion

                            #region Starport
                            if (ctx.styles.worldDetails.HasFlag(WorldDetails.Starport))
                            {
                                string starport = world.Starport.ToString();
                                DrawWorldLabel(ctx, worldTextBackgroundStyle, solidBrush, ctx.styles.worlds.textColor, ctx.styles.StarportPosition, styleRes.StarportFont, starport);
                            }
                            #endregion

                            #region UWP
                            if (renderUWP)
                            {
                                string uwp = world.UWP;
                                solidBrush.Color = ctx.styles.worlds.textColor;

                                ctx.graphics.DrawString(uwp, ctx.styles.hexNumber.Font, solidBrush, ctx.styles.StarportPosition.X, -ctx.styles.StarportPosition.Y, RenderUtil.StringFormatCentered);
                            }
                            #endregion

                            #region Bases
                            // TODO: Mask off background for glyphs
                            if (ctx.styles.worldDetails.HasFlag(WorldDetails.Bases))
                            {
                                string bases = world.Bases;

                                // Special case: Show Zho Naval+Military as diamond
                                if (world.BaseAllegiance == "Zh" && bases == "KM")
                                    bases = "Z";

                                // Base 1
                                bool bottomUsed = false;
                                if (bases.Length > 0)
                                {
                                    Glyph glyph = Glyph.FromBaseCode(world.BaseAllegiance, bases[0]);
                                    if (glyph.Printable)
                                    {
                                        PointF pt = ctx.styles.BaseTopPosition;
                                        if (glyph.Bias == Glyph.GlyphBias.Bottom)
                                        {
                                            pt = ctx.styles.BaseBottomPosition;
                                            bottomUsed = true;
                                        }

                                        solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                        RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, pt.X, pt.Y);
                                    }
                                }

                                // Base 2
                                if (bases.Length > 1)
                                {
                                    Glyph glyph = Glyph.FromBaseCode(world.LegacyAllegiance, bases[1]);
                                    if (glyph.Printable)
                                    {
                                        PointF pt = bottomUsed ? ctx.styles.BaseTopPosition : ctx.styles.BaseBottomPosition;
                                        solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                        RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, pt.X, pt.Y);
                                    }
                                }

                                // Research Stations
                                string rs;
                                if ((rs = world.ResearchStation) != null)
                                {
                                    Glyph glyph = Glyph.FromResearchCode(rs);
                                    solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                    RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, ctx.styles.BaseMiddlePosition.Y);
                                }
                                else if (world.IsReserve)
                                {
                                    Glyph glyph = Glyph.Reserve;
                                    solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                    RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0);
                                }
                                else if (world.IsPenalColony)
                                {
                                    Glyph glyph = Glyph.Prison;
                                    solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                    RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0);
                                }
                                else if (world.IsPrisonExileCamp)
                                {
                                    Glyph glyph = Glyph.ExileCamp;
                                    solidBrush.Color = glyph.IsHighlighted ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;
                                    RenderUtil.DrawGlyph(ctx.graphics, glyph, styleRes, solidBrush, ctx.styles.BaseMiddlePosition.X, 0);
                                }
                            }
                            #endregion
                        }

                        #region Disc
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.Type))
                        {
                            if (isPlaceholder)
                            {
                                DrawWorldLabel(ctx, ctx.styles.placeholder.textBackgroundStyle, solidBrush, ctx.styles.placeholder.textColor, ctx.styles.placeholder.position, ctx.styles.placeholder.Font, ctx.styles.placeholder.content);
                            }
                            else
                            {
                                if (world.Size <= 0)
                                {
                                    #region Asteroid-Belt
                                    if (ctx.styles.worldDetails.HasFlag(WorldDetails.Asteroids))
                                    {
                                        // Basic pattern, with probability varying per position:
                                        //   o o o
                                        //  o o o o
                                        //   o o o

                                        int[] lpx = { -2, 0, 2, -3, -1, 1, 3, -2, 0, 2 };
                                        int[] lpy = { -2, -2, -2, 0, 0, 0, 0, 2, 2, 2 };
                                        float[] lpr = { 0.5f, 0.9f, 0.5f, 0.6f, 0.9f, 0.9f, 0.6f, 0.5f, 0.9f, 0.5f };

                                        solidBrush.Color = ctx.styles.worlds.textColor;

                                        // Random generator is seeded with world location so it is always the same
                                        Random rand = new Random(world.Coordinates.X ^ world.Coordinates.Y);
                                        for (int i = 0; i < lpx.Length; ++i)
                                        {
                                            if (rand.NextDouble() < lpr[i])
                                            {
                                                float px = lpx[i] * 0.035f;
                                                float py = lpy[i] * 0.035f;

                                                float w = 0.04f + (float)rand.NextDouble() * 0.03f;
                                                float h = 0.04f + (float)rand.NextDouble() * 0.03f;

                                                // If necessary, add jitter here
                                                float dx = 0, dy = 0;

                                                ctx.graphics.DrawEllipse(solidBrush,
                                                    px + dx - w / 2, py + dy - h / 2, w, h);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // Just a glyph
                                        solidBrush.Color = ctx.styles.worlds.textColor;
                                        RenderUtil.DrawGlyph(ctx.graphics, Glyph.DiamondX, styleRes, solidBrush, 0.0f, 0.0f);
                                    }
                                    #endregion
                                }
                                else
                                {
                                    XColor penColor, brushColor;
                                    ctx.styles.WorldColors(world, out penColor, out brushColor);

                                    if (!brushColor.IsEmpty)
                                    {
                                        solidBrush.Color = brushColor;
                                        ctx.graphics.DrawEllipse(solidBrush, -0.1f, -0.1f, 0.2f, 0.2f);
                                    }

                                    if (!penColor.IsEmpty)
                                    {
                                        ctx.styles.worldWater.pen.Apply(ref pen);
                                        pen.Color = penColor;
                                        ctx.graphics.DrawEllipse(pen, -0.1f, -0.1f, 0.2f, 0.2f);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // Dotmap
                            solidBrush.Color = ctx.styles.worlds.textColor;
                            ctx.graphics.DrawEllipse(solidBrush, -0.2f, -0.2f, 0.4f, 0.4f);
                        }
                        #endregion
                    }
                }
                else // ctx.styles.useWorldImages
                {
                    float imageRadius = ((world.Size <= 0) ? 0.6f : (0.3f * (world.Size / 5.0f + 0.2f))) / 2;
                    float decorationRadius = imageRadius;

                    if (layer == WorldLayer.Background)
                    {
                        #region Disc
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.Type))
                        {
                            if (isPlaceholder)
                            {
                                DrawWorldLabel(ctx, ctx.styles.placeholder.textBackgroundStyle, solidBrush, ctx.styles.placeholder.textColor, ctx.styles.placeholder.position, ctx.styles.placeholder.Font, ctx.styles.placeholder.content);
                            }
                            else if (world.Size <= 0)
                            {
                                const float scaleX = 1.5f;
                                const float scaleY = 1.0f;
                                XImage img = s_worldImages["Belt"];

                                lock (img)
                                {
                                    ctx.graphics.DrawImage(img, -imageRadius * scaleX, -imageRadius * scaleY, imageRadius * 2 * scaleX, imageRadius * 2 * scaleY);
                                }
                            }
                            else
                            {
                                XImage img;
                                switch (world.Hydrographics)
                                {
                                    default:
                                    case 0x0: img = s_worldImages["Hyd0"]; break;
                                    case 0x1: img = s_worldImages["Hyd1"]; break;
                                    case 0x2: img = s_worldImages["Hyd2"]; break;
                                    case 0x3: img = s_worldImages["Hyd3"]; break;
                                    case 0x4: img = s_worldImages["Hyd4"]; break;
                                    case 0x5: img = s_worldImages["Hyd5"]; break;
                                    case 0x6: img = s_worldImages["Hyd6"]; break;
                                    case 0x7: img = s_worldImages["Hyd7"]; break;
                                    case 0x8: img = s_worldImages["Hyd8"]; break;
                                    case 0x9: img = s_worldImages["Hyd9"]; break;
                                    case 0xA: img = s_worldImages["HydA"]; break;
                                }
                                if (img != null)
                                {
                                    lock (img)
                                    {
                                        ctx.graphics.DrawImage(img, -imageRadius, -imageRadius, imageRadius * 2, imageRadius * 2);
                                    }
                                }
                            }
                        }
                        else
                        {
                            // Dotmap
                            solidBrush.Color = ctx.styles.worlds.textColor;
                            ctx.graphics.DrawEllipse(solidBrush, -0.2f, -0.2f, 0.4f, 0.4f);
                        }
                        #endregion
                    }

                    if (isPlaceholder)
                        return;

                    if (layer == WorldLayer.Foreground)
                    {
                        #region Zone
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.Zone))
                        {
                            if (world.IsAmber || world.IsRed || world.IsBlue)
                            {
                                PenInfo pi =
                                    world.IsAmber ? ctx.styles.amberZone.pen :
                                    world.IsRed ? ctx.styles.redZone.pen : ctx.styles.blueZone.pen;
                                pi.Apply(ref pen);

                                // TODO: Try and accomplish this using dash pattern
                                decorationRadius += 0.1f;
                                ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 5, 80);
                                ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 95, 80);
                                ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 185, 80);
                                ctx.graphics.DrawArc(pen, -decorationRadius, -decorationRadius, decorationRadius * 2, decorationRadius * 2, 275, 80);
                            }
                        }
                        #endregion

                        #region GasGiant
                        if (ctx.styles.worldDetails.HasFlag(WorldDetails.GasGiant))
                        {
                            if (world.GasGiants > 0)
                            {
                                decorationRadius += 0.1f;
                                const float symbolRadius = 0.05f;
                                solidBrush.Color = ctx.styles.worlds.textHighlightColor; ;
                                ctx.graphics.DrawEllipse(solidBrush, decorationRadius - symbolRadius, 0.0f - symbolRadius, symbolRadius * 2, symbolRadius * 2);
                            }
                        }
                        #endregion

                        #region UWP
                        if (renderUWP)
                        {
                            string uwp = world.UWP;
                            solidBrush.Color = ctx.styles.worlds.textColor;

                            using (RenderUtil.SaveState(ctx.graphics))
                            {
                                XMatrix uwpMatrix = new XMatrix();
                                uwpMatrix.TranslatePrepend(decorationRadius, 0.0f);
                                uwpMatrix.ScalePrepend(ctx.styles.worlds.textStyle.Scale.Width, ctx.styles.worlds.textStyle.Scale.Height);
                                uwpMatrix.Multiply(uwpMatrix, XMatrixOrder.Prepend);
                                ctx.graphics.DrawString(uwp, ctx.styles.hexNumber.Font, solidBrush, ctx.styles.StarportPosition.X, -ctx.styles.StarportPosition.Y, RenderUtil.StringFormatCenterLeft);
                            }
                        }
                        #endregion

                        #region Name
                        if (renderName)
                        {
                            string name = world.Name;
                            if (isHiPop)
                                name = name.ToUpperInvariant();

                            using (RenderUtil.SaveState(ctx.graphics))
                            {
                                Color textColor = (isCapital && ctx.styles.worldDetails.HasFlag(WorldDetails.Highlight))
                                    ? ctx.styles.worlds.textHighlightColor : ctx.styles.worlds.textColor;

                                if (ctx.styles.worlds.textStyle.Uppercase)
                                    name = name.ToUpper();

                                decorationRadius += 0.1f;
                                XMatrix imageMatrix = new XMatrix();
                                imageMatrix.TranslatePrepend(decorationRadius, 0.0f);
                                imageMatrix.ScalePrepend(ctx.styles.worlds.textStyle.Scale.Width, ctx.styles.worlds.textStyle.Scale.Height);
                                imageMatrix.TranslatePrepend(ctx.graphics.MeasureString(name, ctx.styles.worlds.Font).Width / 2, 0.0f); // Left align
                                ctx.graphics.MultiplyTransform(imageMatrix, XMatrixOrder.Prepend);

                                DrawWorldLabel(ctx, ctx.styles.worlds.textBackgroundStyle, solidBrush, textColor, ctx.styles.worlds.textStyle.Translation, ctx.styles.worlds.Font, name);
                            }
                        }
                        #endregion
                    }
                }
            }
        }
예제 #7
0
        public static void RenderTile(RenderContext ctx)
        {
            DateTime dtStart = DateTime.Now;
            List<Timer> timers = new List<Timer>();

            if (ctx.resourceManager == null)
                throw new ArgumentNullException("resourceManager");

            if (ctx.graphics == null)
                throw new ArgumentNullException("graphics");

            if (ctx.selector == null)
                throw new ArgumentNullException("selector");

            XSolidBrush solidBrush = new XSolidBrush();
            XPen pen = new XPen(XColor.Empty);

            using (var fonts = new FontCache(ctx.styles))
            {
                #region resources
                lock (s_imageInitLock)
                {
                    if (ctx.styles.useBackgroundImage && s_backgroundImage == null)
                        s_backgroundImage = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Nebula.png"));

                    if (ctx.styles.showRifts && s_riftImage == null)
                        s_riftImage = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Rifts.png")));

                    if (ctx.styles.useGalaxyImage && s_galaxyImage == null) {
                        s_galaxyImage = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Galaxy.png")));
                        s_galaxyImageGray = new ImageHolder(Image.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Galaxy_Gray.png")));
                    }

                    if (ctx.styles.useWorldImages && s_worldImages == null)
                    {
                        s_worldImages = new Dictionary<string, XImage> {
                            { "Hyd0", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd0.png")) },
                            { "Hyd1", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd1.png")) },
                            { "Hyd2", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd2.png")) },
                            { "Hyd3", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd3.png")) },
                            { "Hyd4", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd4.png")) },
                            { "Hyd5", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd5.png")) },
                            { "Hyd6", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd6.png")) },
                            { "Hyd7", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd7.png")) },
                            { "Hyd8", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd8.png")) },
                            { "Hyd9", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Hyd9.png")) },
                            { "HydA", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/HydA.png")) },
                            { "Belt", XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/Candy/Belt.png")) }
                        };
                    }

                    if (ctx.silly && s_sillyImageColor == null)
                    {
                        // Happy face c/o http://bighappyfaces.com/
                        s_sillyImageColor = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst.png"));
                        s_sillyImageGray = XImage.FromFile(ctx.resourceManager.Server.MapPath(@"~/res/AprilFools/Starburst_Gray.png"));
                    }
                }
                #endregion

                timers.Add(new Timer("preload"));
                //////////////////////////////////////////////////////////////
                //
                // Image-Space Rendering
                //
                //////////////////////////////////////////////////////////////

                using (Maps.Rendering.RenderUtil.SaveState(ctx.graphics))
                {
                    if (ctx.clipPath != null)
                    {
                        XMatrix m = ctx.ImageSpaceToWorldSpace;
                        ctx.graphics.MultiplyTransform(m);
                        ctx.graphics.IntersectClip(ctx.clipPath);
                        m.Invert();
                        ctx.graphics.MultiplyTransform(m);
                    }

                    // Fill
                    ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed;
                    solidBrush.Color = ctx.styles.backgroundColor;
                    ctx.graphics.DrawRectangle(solidBrush, 0, 0, ctx.tileSize.Width, ctx.tileSize.Height);

                    //// Draw tile #
                    //using( var font = new Font( FontFamily.GenericSansSerif, 10 ) )
                    //{
                    //  graphics.DrawString( String.Format( "({0},{1})", x, y ), font, foregroundBrush, 0, 0 );
                    //  graphics.DrawString( String.Format( "{0},{1}-{2}x{3}", tileRect.X, tileRect.Y, tileRect.Width, tileRect.Height ), font, foregroundBrush, 0, 20 );
                    //}

                    // Frame it
                    //graphics.DrawRectangle( Pens.Green, 0, 0, tileSize.Width-1, tileSize.Height-1 );
                }

                timers.Add(new Timer("imagespace"));
                //////////////////////////////////////////////////////////////
                //
                // World-Space Rendering
                //
                //////////////////////////////////////////////////////////////

                // Transform from image-space to world-space. Set up a reverse transform as well.
                XMatrix imageSpaceToWorldSpace = ctx.ImageSpaceToWorldSpace;

                XMatrix worldSpaceToImageSpace = imageSpaceToWorldSpace;
                worldSpaceToImageSpace.Invert();

                ctx.graphics.MultiplyTransform(imageSpaceToWorldSpace);

                using (Maps.Rendering.RenderUtil.SaveState(ctx.graphics))
                {

                    //------------------------------------------------------------
                    // Explicit Clipping
                    //------------------------------------------------------------

                    if (ctx.clipPath != null)
                        ctx.graphics.IntersectClip(ctx.clipPath);

                    //ctx.styles.showPseudoRandomStars = true;
                    //------------------------------------------------------------
                    // Backgrounds
                    //------------------------------------------------------------

                    RectangleF galacticBounds = new RectangleF(-14598.67f, -23084.26f, 29234.1133f, 25662.4746f); // TODO: Don't hardcode
                    Rectangle galaxyImageRect = new Rectangle(-18257, -26234, 36551, 32462); // Chosen to match T5 pp.416

                    // This transforms the Linehan galactic structure to the Mikesh galactic structure
                    // See http://travellermap.blogspot.com/2009/03/galaxy-scale-mismatch.html
                    Matrix xformLinehanToMikesh = new Matrix(0.9181034f, 0.0f, 0.0f, 0.855192542f, 120.672432f, 86.34569f);
                    timers.Add(new Timer("prep"));

                    //------------------------------------------------------------
                    // Local background (Nebula)
                    //------------------------------------------------------------
                    #region nebula-background

                    // NOTE: Since alpha texture brushes aren't supported without
                    // creating a new image (slow!) we render the local background
                    // first, then overlay the deep background over it, for
                    // basically the same effect since the alphas sum to 1.

                    if (ctx.styles.useBackgroundImage && galacticBounds.IntersectsWith(ctx.tileRect))
                    {
                        // Image-space rendering, so save current context
                        using (RenderUtil.SaveState(ctx.graphics))
                        {
                            // Never fill outside the galaxy
                            ctx.graphics.IntersectClip(galacticBounds);

                            // Map back to image space so it scales/tiles nicely
                            ctx.graphics.MultiplyTransform(worldSpaceToImageSpace);

                            const float backgroundImageScale = 2.0f;

                            lock (s_backgroundImage)
                            {
                                // Scaled size of the background
                                double w = s_backgroundImage.PixelWidth * backgroundImageScale;
                                double h = s_backgroundImage.PixelHeight * backgroundImageScale;

                                // Offset of the background, relative to the canvas
                                double ox = (float)(-ctx.tileRect.Left * ctx.scale * Astrometrics.ParsecScaleX) % w;
                                double oy = (float)(-ctx.tileRect.Top * ctx.scale * Astrometrics.ParsecScaleY) % h;
                                if (ox > 0) ox -= w;
                                if (oy > 0) oy -= h;

                                // Number of copies needed to cover the canvas
                                int nx = 1 + (int)Math.Floor(ctx.tileSize.Width / w);
                                int ny = 1 + (int)Math.Floor(ctx.tileSize.Height / h);
                                if (ox + nx * w < ctx.tileSize.Width) nx += 1;
                                if (oy + ny * h < ctx.tileSize.Height) ny += 1;

                                for (int x = 0; x < nx; ++x)
                                {
                                    for (int y = 0; y < ny; ++y)
                                    {
                                        ctx.graphics.DrawImage(s_backgroundImage, ox + x * w, oy + y * h, w + 1, h + 1);
                                        //ctx.graphics.DrawRectangle( XPens.Orange, ox + x * w, oy + y * h, w, h );
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                    timers.Add(new Timer("background (nebula)"));

                    //------------------------------------------------------------
                    // Deep background (Galaxy)
                    //------------------------------------------------------------
                    #region galaxy-background
                    if (ctx.styles.useGalaxyImage && ctx.styles.deepBackgroundOpacity > 0f)
                    {
                        using (RenderUtil.SaveState(ctx.graphics))
                        {
                            ctx.graphics.MultiplyTransform(xformLinehanToMikesh);
                            ImageHolder galaxyImage = ctx.styles.lightBackground ? s_galaxyImageGray : s_galaxyImage;
                            lock (galaxyImage)
                            {
                                RenderUtil.DrawImageAlpha(ctx.graphics, ctx.styles.deepBackgroundOpacity, galaxyImage, galaxyImageRect);
                            }
                        }
                    }
                    #endregion
                    timers.Add(new Timer("background (galaxy)"));

                    //------------------------------------------------------------
                    // Pseudo-Random Stars
                    //------------------------------------------------------------
                    #region pseudorandom-stars

                    if (ctx.styles.pseudoRandomStars.visible)
                    {
                        // Render pseudorandom stars based on the tile # and
                        // scale factor. Note that these are positioned in
                        // screen space, not world space.

                        //const int nStars = 75;
                        int nMinStars = ctx.tileSize.Width * ctx.tileSize.Height / 300;
                        int nStars = ctx.scale >= 1 ? nMinStars : (int)(nMinStars / ctx.scale);

                        // NOTE: For performance's sake, three different cases are considered:
                        // (1) Tile is entirely within charted space (most common) - just render
                        //     the pseudorandom stars into the tile
                        // (2) Tile intersects the galaxy bounds - render pseudorandom stars
                        //     into a texture, then fill the galaxy vector with it
                        // (3) Tile is entire outside the galaxy - don't render stars

                        using (RenderUtil.SaveState(ctx.graphics))
                        {
                            ctx.graphics.SmoothingMode = XSmoothingMode.HighQuality;
                            solidBrush.Color = ctx.styles.pseudoRandomStars.fillColor;

                            Random rand = new Random((((int)ctx.tileRect.Left) << 8) ^ (int)ctx.tileRect.Top);
                            for (int i = 0; i < nStars; i++)
                            {
                                float starX = (float)rand.NextDouble() * ctx.tileRect.Width + ctx.tileRect.X;
                                float starY = (float)rand.NextDouble() * ctx.tileRect.Height + ctx.tileRect.Y;
                                float d = (float)rand.NextDouble() * 2;

                                //ctx.graphics.DrawRectangle( fonts.foregroundBrush, starX, starY, (float)( d / ctx.scale * Astrometrics.ParsecScaleX ), (float)( d / ctx.scale * Astrometrics.ParsecScaleY ) );
                                ctx.graphics.DrawEllipse(solidBrush, starX, starY, (float)(d / ctx.scale * Astrometrics.ParsecScaleX), (float)(d / ctx.scale * Astrometrics.ParsecScaleY));
                            }
                        }
                    }
                    #endregion
                    timers.Add(new Timer("pseudorandom"));

                    //------------------------------------------------------------
                    // Rifts in Charted Space
                    //------------------------------------------------------------
                    #region rifts

                    if (ctx.styles.showRifts && ctx.styles.riftOpacity > 0f)
                    {
                        Rectangle riftImageRect;
                        riftImageRect = new Rectangle(-1374, -827, 2769, 1754); // Correct
                        lock (s_riftImage)
                        {
                            RenderUtil.DrawImageAlpha(ctx.graphics, ctx.styles.riftOpacity, s_riftImage, riftImageRect);
                        }
                    }
                    #endregion
                    timers.Add(new Timer("rifts"));

                    //------------------------------------------------------------
                    // April Fool's Day
                    //------------------------------------------------------------
                    #region april-fools

                    if (ctx.silly)
                    {
                        using (RenderUtil.SaveState(ctx.graphics))
                        {
                            // Render in image-space
                            ctx.graphics.MultiplyTransform(worldSpaceToImageSpace);

                            XImage sillyImage = ctx.styles.grayscale ? s_sillyImageGray : s_sillyImageColor;

                            lock (sillyImage)
                            {
                                ctx.graphics.DrawImage(sillyImage, 0, 0, ctx.tileSize.Width, ctx.tileSize.Height);
                            }
                        }
                        timers.Add(new Timer("silly"));
                    }

                    #endregion

                    //------------------------------------------------------------
                    // Macro: Borders object
                    //------------------------------------------------------------
                    #region macro-borders
                    if (ctx.styles.macroBorders.visible)
                    {
                        ctx.styles.macroBorders.pen.Apply(ref pen);
                        ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;
                        foreach (var vec in borderFiles
                            .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject)))
                            .OfType<VectorObject>()
                            .Where(vec => (vec.MapOptions & ctx.options & MapOptions.BordersMask) != 0))
                        {
                            vec.Draw(ctx.graphics, ctx.tileRect, ctx.options, pen);
                        }

                    }
                    #endregion
                    timers.Add(new Timer("macro-borders"));

                    //------------------------------------------------------------
                    // Macro: Route object
                    //------------------------------------------------------------
                    #region macro-routes

                    if (ctx.styles.macroRoutes.visible)
                    {
                        ctx.styles.macroRoutes.pen.Apply(ref pen);
                        ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;
                        foreach (var vec in routeFiles
                            .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject)))
                            .OfType<VectorObject>()
                            .Where(vec => (vec.MapOptions & ctx.options & MapOptions.BordersMask) != 0))
                        {
                            vec.Draw(ctx.graphics, ctx.tileRect, ctx.options, pen);
                        }
                    }
                    #endregion
                    timers.Add(new Timer("macro-routes"));

                    //------------------------------------------------------------
                    // Sector Grid
                    //------------------------------------------------------------
                    #region sector-grid

                    ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed;

                    if (ctx.styles.sectorGrid.visible)
                    {
                        const int gridSlop = 10;
                        ctx.styles.sectorGrid.pen.Apply(ref pen);

                        for (float h = ((float)(Math.Floor((ctx.tileRect.Left) / Astrometrics.SectorWidth) - 1) - Astrometrics.ReferenceSector.X) * Astrometrics.SectorWidth - Astrometrics.ReferenceHex.X; h <= ctx.tileRect.Right + Astrometrics.SectorWidth; h += Astrometrics.SectorWidth)
                            ctx.graphics.DrawLine(pen, h, ctx.tileRect.Top - gridSlop, h, ctx.tileRect.Bottom + gridSlop);

                        for (float v = ((float)(Math.Floor((ctx.tileRect.Top) / Astrometrics.SectorHeight) - 1) - Astrometrics.ReferenceSector.Y) * Astrometrics.SectorHeight - Astrometrics.ReferenceHex.Y; v <= ctx.tileRect.Bottom + Astrometrics.SectorHeight; v += Astrometrics.SectorHeight)
                            ctx.graphics.DrawLine(pen, ctx.tileRect.Left - gridSlop, v, ctx.tileRect.Right + gridSlop, v);
                    }

                    #endregion
                    timers.Add(new Timer("sector grid"));

                    //------------------------------------------------------------
                    // Subsector Grid
                    //------------------------------------------------------------
                    #region subsector-grid
                    ctx.graphics.SmoothingMode = XSmoothingMode.HighSpeed;
                    if (ctx.styles.subsectorGrid.visible)
                    {
                        const int gridSlop = 10;
                        ctx.styles.subsectorGrid.pen.Apply(ref pen);

                        int hmin = (int)Math.Floor(ctx.tileRect.Left / Astrometrics.SubsectorWidth) - 1 - Astrometrics.ReferenceSector.X,
                            hmax = (int)Math.Ceiling((ctx.tileRect.Right + Astrometrics.SubsectorWidth + Astrometrics.ReferenceHex.X) / Astrometrics.SubsectorWidth);
                        for (int hi = hmin; hi <= hmax; ++hi)
                        {
                            if (hi % 4 == 0) continue;
                            float h = hi * Astrometrics.SubsectorWidth - Astrometrics.ReferenceHex.X;
                            ctx.graphics.DrawLine(pen, h, ctx.tileRect.Top - gridSlop, h, ctx.tileRect.Bottom + gridSlop);
                        }

                        int vmin = (int)Math.Floor(ctx.tileRect.Top / Astrometrics.SubsectorHeight) - 1 - Astrometrics.ReferenceSector.Y,
                            vmax = (int)Math.Ceiling((ctx.tileRect.Bottom + Astrometrics.SubsectorHeight + Astrometrics.ReferenceHex.Y) / Astrometrics.SubsectorHeight);
                        for (int vi = vmin; vi <= vmax; ++vi)
                        {
                            if (vi % 4 == 0) continue;
                            float v = vi * Astrometrics.SubsectorHeight - Astrometrics.ReferenceHex.Y;
                            ctx.graphics.DrawLine(pen, ctx.tileRect.Left - gridSlop, v, ctx.tileRect.Right + gridSlop, v);
                        }
                    }
                    #endregion
                    timers.Add(new Timer("subsector grid"));

                    //------------------------------------------------------------
                    // Parsec Grid
                    //------------------------------------------------------------
                    #region parsec-grid
                    // TODO: Optimize - timers indicate this is slow
                    ctx.graphics.SmoothingMode = XSmoothingMode.HighQuality;
                    if (ctx.styles.parsecGrid.visible)
                    {
                        const int parsecSlop = 1;

                        int hx = (int)Math.Floor(ctx.tileRect.Left);
                        int hw = (int)Math.Ceiling(ctx.tileRect.Width);
                        int hy = (int)Math.Floor(ctx.tileRect.Top);
                        int hh = (int)Math.Ceiling(ctx.tileRect.Height);

                        ctx.styles.parsecGrid.pen.Apply(ref pen);

                        switch (ctx.styles.hexStyle)
                        {
                            case HexStyle.Square:
                                for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++)
                                {
                                    float yOffset = ((px % 2) != 0) ? 0.0f : 0.5f;
                                    for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++)
                                    {
                                        // TODO: use RenderUtil.(Square|Hex)Edges(X|Y) arrays
                                        const float inset = 0.1f;
                                        ctx.graphics.DrawRectangle(pen, px + inset, py + inset + yOffset, 1 - inset * 2, 1 - inset * 2);
                                    }
                                }
                                break;

                            case HexStyle.Hex:
                                XPoint[] points = new XPoint[4];
                                for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++)
                                {
                                    double yOffset = ((px % 2) != 0) ? 0.0 : 0.5;
                                    for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++)
                                    {
                                        points[0] = new XPoint(px + -RenderUtil.HEX_EDGE, py + 0.5 + yOffset);
                                        points[1] = new XPoint(px + RenderUtil.HEX_EDGE, py + 1.0 + yOffset);
                                        points[2] = new XPoint(px + 1.0 - RenderUtil.HEX_EDGE, py + 1.0 + yOffset);
                                        points[3] = new XPoint(px + 1.0 + RenderUtil.HEX_EDGE, py + 0.5 + yOffset);
                                        ctx.graphics.DrawLines(pen, points);
                                    }
                                }
                                break;
                            case HexStyle.None:
                                // none
                                break;
                        }

                        if (ctx.styles.numberAllHexes &&
                            ctx.styles.worldDetails.HasFlag(WorldDetails.Hex))
                        {
                            solidBrush.Color = ctx.styles.hexNumber.textColor;
                            for (int px = hx - parsecSlop; px < hx + hw + parsecSlop; px++)
                            {
                                double yOffset = ((px % 2) != 0) ? 0.0 : 0.5;
                                for (int py = hy - parsecSlop; py < hy + hh + parsecSlop; py++)
                                {
                                    Location loc = Astrometrics.CoordinatesToLocation(px + 1, py + 1);
                                    string hex;
                                    switch (ctx.styles.hexCoordinateStyle)
                                    {
                                        default:
                                        case Stylesheet.HexCoordinateStyle.Sector: hex = loc.HexString; break;
                                        case Stylesheet.HexCoordinateStyle.Subsector: hex = loc.SubsectorHexString; break;
                                    }
                                    using (RenderUtil.SaveState(ctx.graphics))
                                    {
                                        XMatrix matrix = new XMatrix();
                                        matrix.TranslatePrepend(px + 0.5f, py + yOffset);
                                        matrix.ScalePrepend(ctx.styles.hexContentScale / Astrometrics.ParsecScaleX, ctx.styles.hexContentScale / Astrometrics.ParsecScaleY);
                                        ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend);
                                        ctx.graphics.DrawString(hex, ctx.styles.hexNumber.Font, solidBrush, 0, 0, RenderUtil.StringFormatTopCenter);
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                    timers.Add(new Timer("parsec grid"));

                    //------------------------------------------------------------
                    // Subsector Names
                    //------------------------------------------------------------
                    #region subsector-names

                    if (ctx.styles.subsectorNames.visible)
                    {
                        solidBrush.Color = ctx.styles.subsectorNames.textColor;
                        foreach (Sector sector in ctx.selector.Sectors)
                        {
                            for (int i = 0; i < 16; i++)
                            {
                                int ssx = i % 4;
                                int ssy = i / 4;

                                Subsector ss = sector[i];
                                if (ss == null || String.IsNullOrEmpty(ss.Name))
                                    continue;

                                Point center = sector.SubsectorCenter(i);
                                RenderUtil.DrawLabel(ctx.graphics, ss.Name, center, ctx.styles.subsectorNames.Font, solidBrush, ctx.styles.subsectorNames.textStyle);
                            }
                        }
                    }

                    #endregion
                    timers.Add(new Timer("subsector names"));

                    //------------------------------------------------------------
                    // Micro: Borders
                    //------------------------------------------------------------
                    #region micro-borders
                    if (ctx.styles.microBorders.visible)
                    {
                        if (ctx.styles.fillMicroBorders)
                            DrawMicroBorders(ctx, BorderLayer.Fill);
                        DrawMicroBorders(ctx, BorderLayer.Stroke);
                    }
                    #endregion
                    timers.Add(new Timer("micro-borders"));

                    //------------------------------------------------------------
                    // Micro: Routes
                    //------------------------------------------------------------
                    #region micro-routes

                    if (ctx.styles.microRoutes.visible)
                        DrawRoutes(ctx, fonts);

                    #endregion
                    timers.Add(new Timer("micro-routes"));

                    //------------------------------------------------------------
                    // Sector Names
                    //------------------------------------------------------------
                    #region sector-names

                    if (ctx.styles.showSomeSectorNames || ctx.styles.showAllSectorNames)
                    {
                        foreach (Sector sector in ctx.selector.Sectors
                            .Where(sector => ctx.styles.showAllSectorNames || (ctx.styles.showSomeSectorNames && sector.Selected))
                            .Where(sector => sector.Names.Any() || sector.Label != null))
                        {
                            solidBrush.Color = ctx.styles.sectorName.textColor;
                            string name = sector.Label ?? sector.Names[0].Text;

                            RenderUtil.DrawLabel(ctx.graphics, name, sector.Center, ctx.styles.sectorName.Font, solidBrush, ctx.styles.sectorName.textStyle);
                        }
                    }

                    #endregion
                    timers.Add(new Timer("sector names"));

                    //------------------------------------------------------------
                    // Mega: Galaxy-Scale Labels
                    //------------------------------------------------------------
                    #region mega-names
                    if (ctx.styles.megaNames.visible)
                    {
                        solidBrush.Color = ctx.styles.megaNames.textColor;
                        foreach (var label in megaLabels)
                        {
                            using (RenderUtil.SaveState(ctx.graphics))
                            {
                                XMatrix matrix = new XMatrix();
                                matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY);
                                matrix.TranslatePrepend(label.position.X, label.position.Y);
                                ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend);

                                XFont font = label.minor ? ctx.styles.megaNames.SmallFont : ctx.styles.megaNames.Font;
                                XSize size = ctx.graphics.MeasureString(label.text, font);
                                ctx.graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text
                                RectangleF textBounds = new RectangleF(0, 0, (float)size.Width * 1.01f, (float)size.Height * 2); // *2 or it gets cut off at high sizes
                                XTextFormatter formatter = new XTextFormatter(ctx.graphics);
                                formatter.Alignment = XParagraphAlignment.Center;
                                formatter.DrawString(label.text, font, solidBrush, textBounds);
                            }
                        }
                    }
                    #endregion
                    timers.Add(new Timer("mega names"));

                    //------------------------------------------------------------
                    // Macro: Government / Rift / Route Names
                    //------------------------------------------------------------
                    #region government-rift-names
                    if (ctx.styles.macroNames.visible)
                    {
                        foreach (var vec in borderFiles
                            .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject)))
                            .OfType<VectorObject>()
                            .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0))
                        {
                            bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor);
                            LabelStyle labelStyle = new LabelStyle();
                            labelStyle.Uppercase = major;
                            XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont;
                            solidBrush.Color = major ? ctx.styles.macroNames.textColor : ctx.styles.macroNames.textHighlightColor;
                            vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle);
                        }

                        foreach (var vec in riftFiles
                            .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject)))
                            .OfType<VectorObject>()
                            .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0))
                        {
                            bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor);
                            LabelStyle labelStyle = new LabelStyle();
                            labelStyle.Rotation = 35;
                            labelStyle.Uppercase = major;
                            XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont;
                            solidBrush.Color = major ? ctx.styles.macroNames.textColor : ctx.styles.macroNames.textHighlightColor;
                            vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle);
                        }

                        if (ctx.styles.macroRoutes.visible)
                        {
                            foreach (var vec in routeFiles
                                .Select(file => ctx.resourceManager.GetXmlFileObject(file, typeof(VectorObject)))
                                .OfType<VectorObject>()
                                .Where(vec => (vec.MapOptions & ctx.options & MapOptions.NamesMask) != 0))
                            {
                                bool major = vec.MapOptions.HasFlag(MapOptions.NamesMajor);
                                LabelStyle labelStyle = new LabelStyle();
                                labelStyle.Uppercase = major;
                                XFont font = major ? ctx.styles.macroNames.Font : ctx.styles.macroNames.SmallFont;
                                solidBrush.Color = major ? ctx.styles.macroRoutes.textColor : ctx.styles.macroRoutes.textHighlightColor;
                                vec.DrawName(ctx.graphics, ctx.tileRect, ctx.options, font, solidBrush, labelStyle);
                            }
                        }

                        if (ctx.options.HasFlag(MapOptions.NamesMinor))
                        {
                            XFont font = ctx.styles.macroNames.MediumFont;
                            solidBrush.Color = ctx.styles.macroRoutes.textHighlightColor;
                            foreach (var label in labels)
                            {
                                using (RenderUtil.SaveState(ctx.graphics))
                                {
                                    XMatrix matrix = new XMatrix();
                                    matrix.ScalePrepend(1.0f / Astrometrics.ParsecScaleX, 1.0f / Astrometrics.ParsecScaleY);
                                    matrix.TranslatePrepend(label.position.X, label.position.Y);
                                    ctx.graphics.MultiplyTransform(matrix, XMatrixOrder.Prepend);

                                    XSize size = ctx.graphics.MeasureString(label.text, font);
                                    ctx.graphics.TranslateTransform(-size.Width / 2, -size.Height / 2); // Center the text
                                    RectangleF textBounds = new RectangleF(0, 0, (float)size.Width, (float)size.Height * 2); // *2 or it gets cut off at high sizes
                                    XTextFormatter formatter = new XTextFormatter(ctx.graphics);
                                    formatter.Alignment = XParagraphAlignment.Center;
                                    formatter.DrawString(label.text, font, solidBrush, textBounds);
                                }
                            }

                        }
                    }

                    #endregion
                    timers.Add(new Timer("macro names"));

                    //------------------------------------------------------------
                    // Macro: Capitals & Home Worlds
                    //------------------------------------------------------------
                    #region capitals-homeworlds

                    if (ctx.styles.capitals.visible && (ctx.options & MapOptions.WorldsMask) != 0)
                    {
                        WorldObjectCollection worlds = ctx.resourceManager.GetXmlFileObject(@"~/res/Worlds.xml", typeof(WorldObjectCollection)) as WorldObjectCollection;
                        if (worlds != null)
                        {
                            solidBrush.Color = ctx.styles.capitals.textColor;
                            foreach (WorldObject world in worlds.Worlds.Where(world => (world.MapOptions & ctx.options) != 0))
                            {
                                world.Paint(ctx.graphics, ctx.tileRect, ctx.options, ctx.styles.capitals.fillColor,
                                    solidBrush, ctx.styles.macroNames.SmallFont);
                            }
                        }
                    }

                    #endregion
                    timers.Add(new Timer("macro worlds"));

                    //------------------------------------------------------------
                    // Micro: Border Labels & Explicit Labels
                    //------------------------------------------------------------
                    #region micro-border-labels

                    if (ctx.styles.showMicroNames)
                        DrawLabels(ctx, fonts);

                    #endregion
                    timers.Add(new Timer("micro-border labels"));
                }

                // End of clipping, so world names are not clipped in jumpmaps.

                //------------------------------------------------------------
                // Worlds
                //------------------------------------------------------------
                #region worlds
                if (ctx.styles.worlds.visible)
                {
                    // TODO: selector may be expensive
                    foreach (World world in ctx.selector.Worlds) { DrawWorld(ctx, fonts, world, WorldLayer.Background); }
                    foreach (World world in ctx.selector.Worlds) { DrawWorld(ctx, fonts, world, WorldLayer.Foreground); }
                }
                #endregion
                timers.Add(new Timer("worlds"));

                //------------------------------------------------------------
                // Unofficial
                //------------------------------------------------------------
                #region unofficial

                if (ctx.styles.dimUnofficialSectors && ctx.styles.worlds.visible)
                {
                    solidBrush.Color = Color.FromArgb(128, ctx.styles.backgroundColor);
                    foreach (Sector sector in ctx.selector.Sectors
                        .Where(sector => !sector.Tags.Contains("Official") && !sector.Tags.Contains("Preserve") && !sector.Tags.Contains("InReview")))
                        ctx.graphics.DrawRectangle(solidBrush, sector.Bounds);
                }

                #endregion

            #if SHOW_TIMING
                using( RenderUtil.SaveState( ctx.graphics ) )
                {
                    XFont font = new XFont( FontFamily.GenericSansSerif, 12, XFontStyle.Regular, new XPdfFontOptions(PdfSharp.Pdf.PdfFontEncoding.Unicode) );
                    ctx.graphics.MultiplyTransform( worldSpaceToImageSpace );
                    double cursorX = 20.0, cursorY = 20.0;
                    DateTime last = dtStart;
                    foreach( Timer s in timers )
                    {
                        TimeSpan ts = s.dt - last;
                        last = s.dt;
                        for( int dx = -1; dx <= 1; ++dx )
                        {
                            for( int dy = -1; dy <= 1; ++dy )
                            {

                                ctx.graphics.DrawString( String.Format( "{0} {1}", Math.Round( ts.TotalMilliseconds ), s.label ), font, XBrushes.Black, cursorX + dx, cursorY + dy );
                            }
                        }
                        ctx.graphics.DrawString( String.Format("{0} {1}", Math.Round(ts.TotalMilliseconds), s.label), font, XBrushes.Yellow, cursorX, cursorY );
                        cursorY += 14;
                    }
                }
            #endif

            }
        }
예제 #8
0
        private static void DrawRoutes(RenderContext ctx, FontCache styleRes)
        {
            using (RenderUtil.SaveState(ctx.graphics))
            {
                ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;
                XPen pen = new XPen(XColor.Empty);
                ctx.styles.microRoutes.pen.Apply(ref pen);
                float baseWidth = ctx.styles.microRoutes.pen.width;

                foreach (Sector sector in ctx.selector.Sectors)
                {
                    foreach (Route route in sector.Routes)
                    {
                        // Compute source/target sectors (may be offset)
                        Point startSector = sector.Location, endSector = sector.Location;
                        startSector.Offset(route.StartOffset);
                        endSector.Offset(route.EndOffset);

                        Location startLocation = new Location(startSector, route.Start);
                        Location endLocation = new Location(endSector, route.End);

                        PointF startPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(startLocation));
                        PointF endPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(endLocation));

                        // If drawing dashed lines twice and the start/end are swapped the
                        // dashes don't overlap correctly. So "sort" the points.
                        if ((startPoint.X > endPoint.X) ||
                            (startPoint.X == endPoint.X) && (startPoint.Y > endPoint.Y))
                        {
                            PointF tmp = startPoint;
                            startPoint = endPoint;
                            endPoint = tmp;
                        }

                        // Shorten line to leave room for world glyph
                        OffsetSegment(ref startPoint, ref endPoint, 0.25f);

                        float? routeWidth = route.Width;
                        Color? routeColor = route.Color;
                        LineStyle? routeStyle = ctx.styles.overrideLineStyle ?? route.Style;

                        SectorStylesheet.StyleResult ssr = sector.ApplyStylesheet("route", route.Allegiance ?? route.Type ?? "Im");
                        routeStyle = routeStyle ?? ssr.GetEnum<LineStyle>("style");
                        routeColor = routeColor ?? ssr.GetColor("color");
                        routeWidth = routeWidth ?? (float?)ssr.GetNumber("width") ?? 1.0f;

                        // In grayscale, convert default color and style to non-default style
                        if (ctx.styles.grayscale && !routeColor.HasValue && !routeStyle.HasValue)
                            routeStyle = LineStyle.Dashed;

                        routeColor = routeColor ?? ctx.styles.microRoutes.pen.color;
                        routeStyle = routeStyle ?? LineStyle.Solid;

                        // Ensure color is visible
                        if (ctx.styles.grayscale || !ColorUtil.NoticeableDifference(routeColor.Value, ctx.styles.backgroundColor))
                            routeColor = ctx.styles.microRoutes.pen.color; // default

                        if (routeStyle.Value == LineStyle.None)
                            continue;

                        pen.Color = routeColor.Value;
                        pen.Width = routeWidth.Value * baseWidth;
                        pen.DashStyle = LineStyleToDashStyle(routeStyle.Value);

                        ctx.graphics.DrawLine(pen, startPoint, endPoint);
                    }
                }
            }
        }
예제 #9
0
        private static void DrawRoutes(RenderContext ctx, FontCache styleRes)
        {
            using (RenderUtil.SaveState(ctx.graphics))
            {
                ctx.graphics.SmoothingMode = XSmoothingMode.AntiAlias;
                XPen pen = new XPen(XColor.Empty);
                ctx.styles.microRoutes.pen.Apply(ref pen);

                foreach (Sector sector in ctx.selector.Sectors)
                {
                    foreach (Route route in sector.Routes)
                    {
                        // Compute source/target sectors (may be offset)
                        Point startSector = sector.Location, endSector = sector.Location;
                        startSector.Offset(route.StartOffset);
                        endSector.Offset(route.EndOffset);

                        Location startLocation = new Location(startSector, route.Start);
                        Location endLocation = new Location(endSector, route.End);

                        PointF startPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(startLocation));
                        PointF endPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(endLocation));

                        // If drawing dashed lines twice and the start/end are swapped the
                        // dashes don't overlap correctly. So "sort" the points.
                        if ((startPoint.X > endPoint.X) ||
                            (startPoint.X == endPoint.X) && (startPoint.Y > endPoint.Y))
                        {
                            PointF tmp = startPoint;
                            startPoint = endPoint;
                            endPoint = tmp;
                        }

                        // HACK
                        if (ctx.styles.grayscale ||
                            !ColorUtil.NoticeableDifference(route.Color, ctx.styles.backgroundColor))
                        {
                            pen.Color = ctx.styles.microRoutes.pen.color; // default
                        }
                        else
                        {
                            pen.Color = route.Color;
                        }

                        switch (route.Style)
                        {
                            default:
                            case Route.RouteStyle.Solid: pen.DashStyle = XDashStyle.Solid; break;
                            case Route.RouteStyle.Dashed: pen.DashStyle = XDashStyle.Dash; break;
                            case Route.RouteStyle.Dotted: pen.DashStyle = XDashStyle.Dot; break;
                        }

                        // HACK
                        if (ctx.styles.grayscale && route.Color != Route.DefaultColor && route.Style == Route.DefaultStyle)
                        {
                            pen.DashStyle = XDashStyle.Dash;
                        }

                        ctx.graphics.DrawLine(pen, startPoint, endPoint);
                    }
                }
            }
        }
예제 #10
0
            public override void Process()
            {
                // NOTE: This (re)initializes a static data structure used for
                // resolving names into sector locations, so needs to be run
                // before any other objects (e.g. Worlds) are loaded.
                ResourceManager resourceManager = new ResourceManager(context.Server);

                //
                // Jump
                //
                int jump = Util.Clamp(GetIntOption("jump", 6), 0, 12);

                //
                // Content & Coordinates
                //
                Selector selector;
                Location loc;
                if (context.Request.HttpMethod == "POST")
                {
                    Sector sector;
                    bool lint = GetBoolOption("lint", defaultValue: false);
                    ErrorLogger errors = new ErrorLogger();
                    sector = GetPostedSector(context.Request, errors);
                    if (lint && !errors.Empty)
                        throw new HttpError(400, "Bad Request", errors.ToString());

                    if (sector == null)
                        throw new HttpError(400, "Bad Request", "Either file or data must be supplied in the POST data.");

                    int hex = GetIntOption("hex", Astrometrics.SectorCentralHex);
                    loc = new Location(new Point(0, 0), hex);
                    selector = new HexSectorSelector(resourceManager, sector, loc.Hex, jump);
                }
                else
                {
                    SectorMap.Milieu map = SectorMap.ForMilieu(resourceManager, GetStringOption("milieu"));

                    if (HasOption("sector") && HasOption("hex"))
                    {
                        string sectorName = GetStringOption("sector");
                        int hex = GetIntOption("hex", 0);
                        Sector sector = map.FromName(sectorName);
                        if (sector == null)
                            throw new HttpError(404, "Not Found", string.Format("The specified sector '{0}' was not found.", sectorName));

                        loc = new Location(sector.Location, hex);
                    }
                    else if (HasLocation())
                    {
                        loc = GetLocation();
                    }
                    else
                    {
                        loc = new Location(map.FromName("Spinward Marches").Location, 1910);
                    }
                    selector = new HexSelector(map, resourceManager, loc, jump);
                }

                //
                // Scale
                //
                double scale = Util.Clamp(GetDoubleOption("scale", 64), MinScale, MaxScale);

                //
                // Options & Style
                //
                MapOptions options = MapOptions.BordersMajor | MapOptions.BordersMinor | MapOptions.ForceHexes;
                Stylesheet.Style style = Stylesheet.Style.Poster;
                ParseOptions(ref options, ref style);

                //
                // Border
                //
                bool border = GetBoolOption("border", defaultValue: true);

                //
                // Clip
                //
                bool clip = GetBoolOption("clip", defaultValue: true);

                //
                // What to render
                //

                RectangleF tileRect = new RectangleF();

                Point coords = Astrometrics.LocationToCoordinates(loc);
                tileRect.X = coords.X - jump - 1;
                tileRect.Width = jump + 1 + jump;
                tileRect.Y = coords.Y - jump - 1;
                tileRect.Height = jump + 1 + jump;

                // Account for jagged hexes
                tileRect.Y += (coords.X % 2 == 0) ? 0 : 0.5f;
                tileRect.Inflate(0.35f, 0.15f);

                Size tileSize = new Size((int)Math.Floor(tileRect.Width * scale * Astrometrics.ParsecScaleX), (int)Math.Floor(tileRect.Height * scale * Astrometrics.ParsecScaleY));

                // Construct clipping path
                List<Point> clipPath = new List<Point>(jump * 6 + 1);
                Point cur = coords;
                for (int i = 0; i < jump; ++i)
                {
                    // Move J parsecs to the upper-left (start of border path logic)
                    cur = Astrometrics.HexNeighbor(cur, 1);
                }
                clipPath.Add(cur);
                for (int dir = 0; dir < 6; ++dir)
                {
                    for (int i = 0; i < jump; ++i)
                    {
                        cur = Astrometrics.HexNeighbor(cur, (dir + 3) % 6); // Clockwise from upper left
                        clipPath.Add(cur);
                    }
                }

                Stylesheet styles = new Stylesheet(scale, options, style);

                // If any names are showing, show them all
                if (styles.worldDetails.HasFlag(WorldDetails.KeyNames))
                    styles.worldDetails |= WorldDetails.AllNames;

                // Compute path
                float[] edgeX, edgeY;
                RenderUtil.HexEdges(styles.hexStyle == HexStyle.Square ? PathUtil.PathType.Square : PathUtil.PathType.Hex,
                    out edgeX, out edgeY);
                PointF[] boundingPathCoords;
                byte[] boundingPathTypes;
                PathUtil.ComputeBorderPath(clipPath, edgeX, edgeY, out boundingPathCoords, out boundingPathTypes);

                RenderContext ctx = new RenderContext(resourceManager, selector, tileRect, scale, options, styles, tileSize);
                ctx.DrawBorder = border;
                ctx.ClipOutsectorBorders = true;

                // TODO: Widen path to allow for single-pixel border
                ctx.ClipPath = clip ? new XGraphicsPath(boundingPathCoords, boundingPathTypes, XFillMode.Alternate) : null;
                ProduceResponse(context, "Jump Map", ctx, tileSize, transparent: clip);
            }
예제 #11
0
 protected void ProduceResponse(HttpContext context, string title, RenderContext ctx, Size tileSize,
     int rot = 0, float translateX = 0, float translateY = 0,
     bool transparent = false)
 {
     ProduceResponse(context, this, title, ctx, tileSize, rot, translateX, translateY, transparent,
         (context.Items["RouteData"] as System.Web.Routing.RouteData).Values);
 }
예제 #12
0
            private static void RenderToGraphics(RenderContext ctx, int rot, float translateX, float translateY, XGraphics graphics)
            {
                graphics.TranslateTransform(translateX, translateY);
                graphics.RotateTransform(rot * 90);

                if (ctx.DrawBorder && ctx.ClipPath != null)
                {
                    using (RenderUtil.SaveState(graphics))
                    {
                        // Render border in world space
                        XMatrix m = ctx.ImageSpaceToWorldSpace;
                        graphics.MultiplyTransform(m);
                        XPen pen = new XPen(ctx.Styles.imageBorderColor, 0.2f);

                        // PdfSharp can't ExcludeClip so we take advantage of the fact that we know
                        // the path starts on the left edge and proceeds clockwise. We extend the
                        // path with a counterclockwise border around it, then use that to exclude
                        // the original path's region for rendering the border.
                        ctx.ClipPath.Flatten();
                        RectangleF bounds = PathUtil.Bounds(ctx.ClipPath);
                        bounds.Inflate(2 * (float)pen.Width, 2 * (float)pen.Width);
                        List<byte> types = new List<byte>(ctx.ClipPath.Internals.GdiPath.PathTypes);
                        List<PointF> points = new List<PointF>(ctx.ClipPath.Internals.GdiPath.PathPoints);

                        PointF key = points[0];
                        points.Add(new PointF(bounds.Left, key.Y)); types.Add(1);
                        points.Add(new PointF(bounds.Left, bounds.Bottom)); types.Add(1);
                        points.Add(new PointF(bounds.Right, bounds.Bottom)); types.Add(1);
                        points.Add(new PointF(bounds.Right, bounds.Top)); types.Add(1);
                        points.Add(new PointF(bounds.Left, bounds.Top)); types.Add(1);
                        points.Add(new PointF(bounds.Left, key.Y)); types.Add(1);
                        points.Add(new PointF(key.X, key.Y)); types.Add(1);

                        XGraphicsPath path = new XGraphicsPath(points.ToArray(), types.ToArray(), XFillMode.Winding);
                        graphics.IntersectClip(path);
                        graphics.DrawPath(pen, ctx.ClipPath);
                    }
                }

                using (RenderUtil.SaveState(graphics))
                {
                    ctx.Render(graphics);
                }
            }
예제 #13
0
            protected void ProduceResponse(HttpContext context, ITypeAccepter accepter, string title, RenderContext ctx, Size tileSize,
                int rot = 0, float translateX = 0, float translateY = 0,
                bool transparent = false, IDictionary<string, object> queryDefaults = null)
            {
                // New-style Options
                // TODO: move to ParseOptions (maybe - requires options to be parsed after stylesheet creation?)
                if (GetBoolOption("sscoords", queryDefaults: queryDefaults, defaultValue: false))
                    ctx.Styles.hexCoordinateStyle = Stylesheet.HexCoordinateStyle.Subsector;

                if (GetBoolOption("allhexes", queryDefaults: queryDefaults, defaultValue: false))
                    ctx.Styles.numberAllHexes = true;

                if (!GetBoolOption("routes", queryDefaults: queryDefaults, defaultValue: true))
                {
                    ctx.Styles.macroRoutes.visible = false;
                    ctx.Styles.microRoutes.visible = false;
                }

                ctx.Styles.dimUnofficialSectors = GetBoolOption("dimunofficial", queryDefaults: queryDefaults, defaultValue: false);
                ctx.Styles.droyneWorlds.visible = GetBoolOption("dw", queryDefaults: queryDefaults, defaultValue: false);

                double devicePixelRatio = GetDoubleOption("dpr", defaultValue: 1, queryDefaults: queryDefaults);
                if (devicePixelRatio <= 0)
                    devicePixelRatio = 1;

                if (accepter.Accepts(context, MediaTypeNames.Application.Pdf))
                {
                    using (var document = new PdfDocument())
                    {
                        document.Version = 14; // 1.4 for opacity
                        document.Info.Title = title;
                        document.Info.Author = "Joshua Bell";
                        document.Info.Creator = "TravellerMap.com";
                        document.Info.Subject = DateTime.Now.ToString("F", CultureInfo.InvariantCulture);
                        document.Info.Keywords = "The Traveller game in all forms is owned by Far Future Enterprises. Copyright (C) 1977 - 2015 Far Future Enterprises. Traveller is a registered trademark of Far Future Enterprises.";

                        // TODO: Credits/Copyright
                        // This is close, but doesn't define the namespace correctly:
                        // document.Info.Elements.Add( new KeyValuePair<string, PdfItem>( "/photoshop/Copyright", new PdfString( "HelloWorld" ) ) );

                        PdfPage page = document.AddPage();

                        // NOTE: only PageUnit currently supported in XGraphics is Points
                        page.Width = XUnit.FromPoint(tileSize.Width);
                        page.Height = XUnit.FromPoint(tileSize.Height);

                        XGraphics gfx = XGraphics.FromPdfPage(page);

                        RenderToGraphics(ctx, rot, translateX, translateY, gfx);

                        using (var stream = new MemoryStream())
                        {
                            document.Save(stream, closeStream: false);

                            context.Response.ContentType = MediaTypeNames.Application.Pdf;
                            context.Response.AddHeader("content-length", stream.Length.ToString());
                            context.Response.AddHeader("content-disposition", "inline;filename=\"map.pdf\"");
                            context.Response.BinaryWrite(stream.ToArray());
                            context.Response.Flush();
                            context.Response.Close();
                        }

                        return;
                    }
                }

                using (var bitmap = new Bitmap((int)Math.Floor(tileSize.Width * devicePixelRatio), (int)Math.Floor(tileSize.Height * devicePixelRatio), PixelFormat.Format32bppArgb))
                {
                    if (transparent)
                        bitmap.MakeTransparent();

                    using (var g = Graphics.FromImage(bitmap))
                    {
                        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

                        using (var graphics = XGraphics.FromGraphics(g, new XSize(tileSize.Width * devicePixelRatio, tileSize.Height * devicePixelRatio)))
                        {
                            graphics.ScaleTransform(devicePixelRatio);

                            RenderToGraphics(ctx, rot, translateX, translateY, graphics);
                        }
                    }

                    bool dataURI = GetBoolOption("datauri", queryDefaults: queryDefaults, defaultValue: false);
                    MemoryStream ms = null;
                    if (dataURI)
                        ms = new MemoryStream();

                    BitmapResponse(context.Response, dataURI ? ms : context.Response.OutputStream, ctx.Styles, bitmap, transparent ? Util.MediaTypeName_Image_Png : null);

                    if (dataURI)
                    {
                        string contentType = context.Response.ContentType;
                        context.Response.ContentType = System.Net.Mime.MediaTypeNames.Text.Plain;
                        ms.Seek(0, SeekOrigin.Begin);

                        context.Response.Output.Write("data:");
                        context.Response.Output.Write(contentType);
                        context.Response.Output.Write(";base64,");
                        context.Response.Output.Flush();

                        byte[] buffer = new byte[4096];
                        System.Security.Cryptography.ICryptoTransform transform = new System.Security.Cryptography.ToBase64Transform();
                        using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(context.Response.OutputStream, transform, System.Security.Cryptography.CryptoStreamMode.Write))
                        {
                            int bytesRead;
                            while ((bytesRead = ms.Read(buffer, 0, buffer.Length)) > 0)
                                cs.Write(buffer, 0, bytesRead);
                            cs.FlushFinalBlock();
                        }
                        context.Response.OutputStream.Flush();
                    }
                }
            }
예제 #14
0
            public override void Process()
            {
                // NOTE: This (re)initializes a static data structure used for
                // resolving names into sector locations, so needs to be run
                // before any other objects (e.g. Worlds) are loaded.
                ResourceManager resourceManager = new ResourceManager(context.Server);

                Selector selector;
                RectangleF tileRect = new RectangleF();
                MapOptions options = MapOptions.SectorGrid | MapOptions.SubsectorGrid | MapOptions.BordersMajor | MapOptions.BordersMinor | MapOptions.NamesMajor | MapOptions.NamesMinor | MapOptions.WorldsCapitals | MapOptions.WorldsHomeworlds;
                Stylesheet.Style style = Stylesheet.Style.Poster;
                ParseOptions(ref options, ref style);
                string title;
                bool clipOutsectorBorders;

                if (HasOption("x1") && HasOption("x2") &&
                    HasOption("y1") && HasOption("y2"))
                {
                    // Arbitrary rectangle

                    int x1 = GetIntOption("x1", 0);
                    int x2 = GetIntOption("x2", 0);
                    int y1 = GetIntOption("y1", 0);
                    int y2 = GetIntOption("y2", 0);

                    tileRect.X = Math.Min(x1, x2);
                    tileRect.Y = Math.Min(y1, y2);
                    tileRect.Width = Math.Max(x1, x2) - tileRect.X;
                    tileRect.Height = Math.Max(y1, y2) - tileRect.Y;

                    SectorMap.Milieu map = SectorMap.ForMilieu(resourceManager, GetStringOption("milieu"));
                    selector = new RectSelector(map, resourceManager, tileRect);
                    selector.Slop = false;

                    tileRect.Offset(-1, -1);
                    tileRect.Width += 1;
                    tileRect.Height += 1;

                    title = string.Format("Poster ({0},{1}) - ({2},{3})", x1, y1, x2, y2);
                    clipOutsectorBorders = true;
                }
                else if (HasOption("domain"))
                {
                    string domain = GetStringOption("domain");
                    int x, y, w = 2, h = 2;
                    switch (domain.ToLowerInvariant())
                    {
                        case "deneb": x = -4; y = -1; title = "Domain of Deneb"; break;
                        case "vland": x = -2; y = -1; title = "Domain of Vland"; break;
                        case "ilelish": x = -2; y = 1; title = "Domain of Ilelish"; break;
                        case "antares": x = 0; y = -2; title = "Domain of Antares"; break;
                        case "sylea": x = 0; y = 0; title = "Domain of Sylea"; break;
                        case "sol": x = 0; y = 2; title = "Domain of Sol"; break;
                        case "gateway": x = 2; y = 0; title = "Domain of Gateway"; break;

                        // And these aren't domains, but...
                        case "foreven": x = -6; y = -1; title = "Land Grant / Foreven"; break;
                        case "imperium": x = -4; y = -1; w = 7; h = 5; title = "Third Imperium"; break;
                        case "solomani": x = -2; y = 2; w = 5; h = 3; title = "Solomani Confederacy"; break;
                        case "zhodani": x = -8; y = -3; w = 5; h = 3; title = "Zhodani Consulate"; break;
                        case "hive":
                        case "hiver": x = 2; y = 1; w = 6; h = 4; title = "Hiver Federation"; break;
                        case "aslan": x = -8; y = 1; w = 7; h = 4; title = "Aslan Hierate"; break;
                        case "vargr": x = -4; y = -4; w = 8; h = 3; title = "Vargr Extents"; break;
                        // TODO: K'kree
                        // TODO: Zhodani provinces

                        case "jg": x = 160; y = 0; w = 2; h = 2; title = "Judges Guild"; break;

                        default:
                            throw new HttpError(404, "Not Found", string.Format("Unknown domain: {0}", domain));
                    }

                    int x1 = x * Astrometrics.SectorWidth - Astrometrics.ReferenceHex.X + 1;
                    int y1 = y * Astrometrics.SectorHeight - Astrometrics.ReferenceHex.Y + 1;
                    int x2 = x1 + w * Astrometrics.SectorWidth - 1;
                    int y2 = y1 + h * Astrometrics.SectorHeight - 1;

                    tileRect.X = Math.Min(x1, x2);
                    tileRect.Y = Math.Min(y1, y2);
                    tileRect.Width = Math.Max(x1, x2) - tileRect.X;
                    tileRect.Height = Math.Max(y1, y2) - tileRect.Y;

                    SectorMap.Milieu map = SectorMap.ForMilieu(resourceManager, GetStringOption("milieu"));
                    selector = new RectSelector(map, resourceManager, tileRect);
                    selector.Slop = false;

                    tileRect.Offset(-1, -1);
                    tileRect.Width += 1;
                    tileRect.Height += 1;

                    // Account for jagged hexes
                    tileRect.Height += 0.5f;
                    tileRect.Inflate(0.25f, 0.10f);
                    if (style == Stylesheet.Style.Candy)
                        tileRect.Width += 0.75f;

                    clipOutsectorBorders = true;
                }
                else
                {
                    // Sector - either POSTed or specified by name
                    Sector sector = null;
                    options = options & ~MapOptions.SectorGrid;

                    if (context.Request.HttpMethod == "POST")
                    {
                        bool lint = GetBoolOption("lint", defaultValue: false);
                        ErrorLogger errors = new ErrorLogger();
                        sector = GetPostedSector(context.Request, errors);
                        if (lint && !errors.Empty)
                            throw new HttpError(400, "Bad Request", errors.ToString());

                        if (sector == null)
                            throw new HttpError(400, "Bad Request", "Either file or data must be supplied in the POST data.");

                        title = "User Data";

                        // TODO: Suppress all OTU rendering.
                        options = options & ~MapOptions.WorldsHomeworlds & ~MapOptions.WorldsCapitals;
                    }
                    else
                    {
                        string sectorName = GetStringOption("sector");
                        if (sectorName == null)
                            throw new HttpError(400, "Bad Request", "No sector specified.");

                        SectorMap.Milieu map = SectorMap.ForMilieu(resourceManager, GetStringOption("milieu"));

                        sector = map.FromName(sectorName);
                        if (sector == null)
                            throw new HttpError(404, "Not Found", string.Format("The specified sector '{0}' was not found.", sectorName));

                        title = sector.Names[0].Text;
                    }

                    if (sector != null && HasOption("subsector") && GetStringOption("subsector").Length > 0)
                    {
                        options = options & ~MapOptions.SubsectorGrid;
                        string subsector = GetStringOption("subsector");
                        int index = sector.SubsectorIndexFor(subsector);
                        if (index == -1)
                            throw new HttpError(404, "Not Found", string.Format("The specified subsector '{0}' was not found.", subsector));

                        selector = new SubsectorSelector(resourceManager, sector, index);

                        tileRect = sector.SubsectorBounds(index);

                        options &= ~(MapOptions.SectorGrid | MapOptions.SubsectorGrid);

                        title = string.Format("{0} - Subsector {1}", title, 'A' + index);
                    }
                    else if (sector != null && HasOption("quadrant") && GetStringOption("quadrant").Length > 0)
                    {
                        string quadrant = GetStringOption("quadrant");
                        int index;
                        switch (quadrant.ToLowerInvariant())
                        {
                            case "alpha": index = 0; quadrant = "Alpha"; break;
                            case "beta": index = 1; quadrant = "Beta"; break;
                            case "gamma": index = 2; quadrant = "Gamma"; break;
                            case "delta": index = 3; quadrant = "Delta"; break;
                            default:
                                throw new HttpError(400, "Bad Request", string.Format("The specified quadrant '{0}' is invalid.", quadrant));
                        }

                        selector = new QuadrantSelector(resourceManager, sector, index);
                        tileRect = sector.QuadrantBounds(index);

                        options &= ~(MapOptions.SectorGrid | MapOptions.SubsectorGrid | MapOptions.SectorsMask);

                        title = string.Format("{0} - {1} Quadrant", title, quadrant);
                    }
                    else
                    {
                        selector = new SectorSelector(resourceManager, sector);
                        tileRect = sector.Bounds;

                        options &= ~(MapOptions.SectorGrid);
                    }

                    // Account for jagged hexes
                    tileRect.Height += 0.5f;
                    tileRect.Inflate(0.25f, 0.10f);
                    if (style == Stylesheet.Style.Candy)
                        tileRect.Width += 0.75f;
                    clipOutsectorBorders = false;
                }

                const double NormalScale = 64; // pixels/parsec - standard subsector-rendering scale
                double scale = Util.Clamp(GetDoubleOption("scale", NormalScale), MinScale, MaxScale);

                int rot = GetIntOption("rotation", 0) % 4;
                bool thumb = GetBoolOption("thumb", false);

                Stylesheet stylesheet = new Stylesheet(scale, options, style);

                Size tileSize = new Size((int)Math.Floor(tileRect.Width * scale * Astrometrics.ParsecScaleX), (int)Math.Floor(tileRect.Height * scale * Astrometrics.ParsecScaleY));

                if (thumb)
                {
                    tileSize.Width = (int)Math.Floor(16 * tileSize.Width / scale);
                    tileSize.Height = (int)Math.Floor(16 * tileSize.Height / scale);
                    scale = 16;
                }

                int bitmapWidth = tileSize.Width, bitmapHeight = tileSize.Height;
                float translateX = 0, translateY = 0;
                switch (rot)
                {
                    case 1: // 90 degrees clockwise
                        Util.Swap(ref bitmapWidth, ref bitmapHeight);
                        translateX = bitmapWidth;
                        break;
                    case 2: // 180 degrees
                        translateX = bitmapWidth; translateY = bitmapHeight;
                        break;
                    case 3: // 270 degrees clockwise
                        Util.Swap(ref bitmapWidth, ref bitmapHeight);
                        translateY = bitmapHeight;
                        break;
                }

                RenderContext ctx = new RenderContext(resourceManager, selector, tileRect, scale, options, stylesheet, tileSize);
                ctx.ClipOutsectorBorders = clipOutsectorBorders;
                ProduceResponse(context, title, ctx, new Size(bitmapWidth, bitmapHeight), rot, translateX, translateY);
            }