/// <summary> /// Write out the image associated with the symbol. /// </summary> /// <param name="ms"> /// The symbol for which to write out the image. /// </param> /// <param name="rootName"> /// The name associated with the symbol. /// </param> private void WriteImage(MilSymbol ms, string rootName) { const double Scale = 0.333333; var ct = new CompositeTransform(); ct.ScaleX = ct.ScaleY = Scale; ct.TranslateX = -ms.Bounds.Left * Scale; ct.TranslateY = -ms.Bounds.Top * Scale; ms.RenderTransform = ct; target.Width = Scale * ms.Bounds.Width; target.Height = Scale * ms.Bounds.Height; var path = new System.Windows.Shapes.Path { Fill = new SolidColorBrush(Colors.White), Stroke = new SolidColorBrush(Colors.White), StrokeThickness = 3.0, Data = new RectangleGeometry { Rect = new Rect(0, 0, target.Width - 1, target.Height - 1) } }; target.Children.Add(path); target.Children.Add(ms); #if DO_WRITES this.SaveImage(rootName); #endif target.Children.Clear(); }
/// <summary> /// Process the given appendix. /// </summary> /// <param name="append"> /// The name of the appendix. /// </param> /// <param name="framed"> /// Whether or not the symbols are framed. /// </param> private void ProcessSymbolCollection(string append, bool framed) { const double Scale = 1.0; // Since some appendices cross reference symbols, // in other appendices, we'll check to make sure // we haven't already drawn a particular symbol code. IList <string> symList = new List <string>(); var affiliations = new[] { "U", "F", "N", "H" }; var keys = MilAppendix.Keys(append); foreach (var ap in keys) { MilSymbol ms; var sc = ap; if (symList.Contains(sc)) { continue; } symList.Add(sc); // There is no affiliation for weather var schemeKey = CodingScheme.GetCode(sc); if (schemeKey == CodingScheme.Weather) { ms = new MilSymbol(sc, Scale, "X=67.8"); this.ProcessSymbol(ms, sc); continue; } if (schemeKey == CodingScheme.TacticalGraphics) { sc = sc.Substring(0, 1) + "H" + sc.Substring(2, 1) + "A" + sc.Substring(4); ms = new MilSymbol(sc, Scale, "H=HH;H1=H1;W=W;W1=W1;T=TT;N=N;X=XX;V=VV;C=CC;Y=YY;Q=-60.0"); this.ProcessSymbol(ms, sc); continue; } if (!framed) { sc = sc.Substring(0, 2) + "X" + sc.Substring(3); } foreach (var c in affiliations) { sc = sc[0] + c + sc[2] + "P" + sc.Substring(4, 10) + "E"; ms = new MilSymbol(sc, Scale, null, null); this.ProcessSymbol(ms, sc); } } }
/// <summary> /// Generates the CXML for the symbol. /// </summary> /// <param name="ms"> /// The symbol for which to generate the CXML. /// </param> /// <param name="name"> /// The name to associate with the symbol. /// </param> private void ProcessSymbol(MilSymbol ms, string name) { if (ms.Empty) { return; } var rootName = name.Substring(0, 10); this.WriteCxml(ms, rootName); this.WriteImage(ms, rootName); }
/// <summary> /// Plots a symbol to the canvas. /// </summary> /// <param name="cv">The canvas.</param> /// <param name="ms">The military symbol.</param> /// <param name="x">The x location on the canvas.</param> /// <param name="y">The y location on the canvas.</param> private static void DrawSymbol(Panel cv, MilSymbol ms, double x, double y) { if (!ms.Empty) { ms.SetValue(Canvas.TopProperty, y); ms.SetValue(Canvas.LeftProperty, x); // Not necessarily a good idea, in general, to pre-generate a bunch of tooltips ToolTipService.SetToolTip(ms, ms.SymbolCode + "\n" + GenerateTooltip(ms.SymbolCode)); cv.Children.Add(ms); } }
public void MilHatsTest() { var ms = new MilSymbol("IHGqSCC---" + "BC" + "qqq"); var tf = new[] { "BC", "DG", "EI", "GM" }; var heights = new[] { -253, -293.3999, -253, -320.3999 }; for (int i = 0; i < tf.Length; i++) { string sc = "IHGqSCC---" + tf[i] + "qqq"; double d = MilHats.Generate(ms, sc); Assert.IsTrue(Math.Abs(heights[i] - d) < 0.0001); } }
/// <summary> /// Generate the text block representing the possible echelon value /// </summary> /// <param name="ms">the symbol to which the generated rendering is attached.</param> /// <param name="symbolCode">the symbol code so we know which echelon to use</param> /// <returns>the height of the echelon text string above the symbol</returns> internal static double Generate(MilSymbol ms, string symbolCode) { // This is the maximum height generated by this combination of pieces Rect r = ms.BaseRect; if (r.IsEmpty) { return(0); } double top = r.Top; // Echelon ignored if modifier index represents Installation or Mobility char mi = ModifierCode.GetCode(symbolCode); if (mi == ModifierCode.Installation || mi == ModifierCode.Mobility || mi == ModifierCode.Towed) { return(top); } char echelon = GetCode(symbolCode); if (!Echelons.ContainsKey(echelon)) { return(top); } var tb = new TextBlock { Style = SymbolData.GetStyle("EchelonLabels") }; var height = (double)tb.GetValue(TextBlock.FontSizeProperty); tb.Text = Echelons[echelon]; if (string.IsNullOrEmpty(tb.Text)) { return(top); } // Make the text a little closer (0.90) to the symbol than it would otherwise be. top -= 0.90 * height; tb.FindTextExtent(); tb.SetValue(Canvas.TopProperty, top); // this positions the top line just above the symbol, I think tb.SetValue(Canvas.LeftProperty, -tb.Width / 2); // Add the echelon to the symbol ms.AddChild("Echelon", tb); return(top); // return the new height so that other decorations can be properly placed }
/// <summary> /// A test to check out the various mobility settings and some echelon markings /// </summary> private static void PlotAllMobility() { Canvas cv = GetCanvas(); if (cv == null) { return; } double x = Edge; double y = Tight; var affiliations = new[] { "U", "F", "N", "H" }; var categoryBattleDimension = new[] { "A", "G", "U", "F" }; var mob = new[] { "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "NS", "NL", "H-", "HB", "AA", "BC", "CE", "DG", "EI", "FK", "GM" }; foreach (string bd in categoryBattleDimension) { foreach (string ee in affiliations) { foreach (string m in mob) { string sc; if (bd == "F") { sc = "S" + ee + bd + "AN-----" + m + "AF*"; } else { sc = "I" + ee + bd + "ASRU---" + m + "AF*"; } var ms = new MilSymbol(sc, Scale, null, null); DrawSymbol(cv, ms, x, y); if ((x += Tight) > 11 * Tight) { x = Edge; y += Loose; } } } } }
/// <summary> /// Simple test showing that changing some values causes symbol to update. /// </summary> /// <param name="sender">This parameter is not used.</param> /// <param name="e">This parameter is also not used.</param> private void TimerTick(object sender, object e) { if (this.milSymbol == null) { this.milSymbol = new MilSymbol( "IUGPSRU---DKCAE", labelString: "X=The default;V= label font;T=is Arial;H=World", scale: 2 * Scale) { LabelG = "Hello" }; DrawSymbol(GetCanvas(), this.milSymbol, 300, 300); return; } this.milSymbol.Angle += 2; int count = (int)(this.milSymbol.Angle % 360) / 36; this.milSymbol.SymbolCode = "I" + CommonAffiliation[count % 4] + "GPSRU---DKCAE"; this.milSymbol.LabelString = "F=" + this.milSymbol.Angle + "°"; // Should be changing tooltip here too this.milSymbol.SetValue(Canvas.TopProperty, 300 + (100 * Math.Sin(this.milSymbol.Angle * Math.PI / 180.0))); this.milSymbol.SetValue(Canvas.LeftProperty, 300 + (100 * Math.Cos(this.milSymbol.Angle * Math.PI / 180.0))); }
/// <summary> /// Writes out the CXML file. /// </summary> /// <param name="ms"> /// The symbol for which to generate the CXML. /// </param> /// <param name="rootName"> /// The name to use for the symbol. /// </param> private void WriteCxml(MilSymbol ms, string rootName) { var symbolCode = (CodingScheme.GetCode(ms.SymbolCode) != CodingScheme.Weather) ? rootName + "*****" : ms.SymbolCode; var description = MilAppendix.Description(symbolCode); var lines = description.Split(new[] { '\n' }); var lineCount = lines.Length - 1; // the last line is empty var sb = new StringBuilder(); sb.AppendFormat(@"<Item Id=""{0}"" Name=""{1}"" Img=""Symbols\{1}.png"" Href="""">", this.index++, rootName); sb.AppendLine(); if (lineCount > 0) { sb.AppendFormat(@" <Description>""{0}""</Description>", lines[lineCount - 1].Trim(new[] { ' ', '\n', '\r' })); sb.AppendLine(); } else { sb.AppendFormat(@" <Description>""""</Description>"); sb.AppendLine(); } sb.AppendLine(@" <Facets>"); sb.AppendLine(@" <Facet Name=""Affiliation"">"); sb.AppendFormat(@" <String Value=""{0}"" />", StandardIdentity.GetName(symbolCode)); sb.AppendLine(); sb.AppendLine(@" </Facet>"); sb.AppendLine(@" <Facet Name=""Battle Dimension"">"); sb.AppendFormat(@" <String Value=""{0}"" />", CategoryBattleDimension.GetName(symbolCode)); sb.AppendLine(); sb.AppendLine(@" </Facet>"); if (lineCount > 2) { sb.AppendLine(@" <Facet Name=""Type"">"); sb.AppendFormat(@" <String Value=""{0}"" />", lines[2].Trim(new[] { ' ', '\n', '\r' })); sb.AppendLine(); sb.AppendLine(@" </Facet>"); } sb.AppendLine(@" <Facet Name=""Coding Scheme"">"); sb.AppendFormat(@" <String Value=""{0}"" />", CodingScheme.GetName(symbolCode)); sb.AppendLine(); sb.AppendLine(@" </Facet>"); if (lineCount - 1 > 3) { sb.AppendLine(@" <Facet Name=""Key Phrases"">"); for (var i = 3; i < lineCount - 1; i++) { sb.AppendFormat(@" <String Value=""{0}"" />", lines[i].Trim(new[] { ' ', '\n', '\r' })); sb.AppendLine(); } sb.AppendLine(@" </Facet>"); } sb.AppendLine(@" </Facets>"); sb.AppendLine(@"</Item>"); #if DO_WRITES this.cxmlStream.Write(sb.ToString()); #endif }
/// <summary> /// Plots all of the core symbols that are in Appendix D. /// Since not all symbol codes are non-blank, there are some blanks in the output. /// Refer to MIL-STD 2525C for comparison. /// </summary> /// <param name="append"> /// The appendix from which to get the symbols. /// </param> /// <param name="framed"> /// Whether or not the symbols are framed. /// </param> private static void PlotAllSymbols(string append, bool framed) { Canvas cv = GetCanvas(); if (cv == null) { return; } // Since some appendices cross reference symbols, // in other appendices, we'll check to make sure // we haven't already drawn a particular symbol code. IList <string> symList = new List <string>(); double x = Edge; double y = Edge; var affiliations = new[] { "U", "F", "N", "H" }; bool first = true; var keys = MilAppendix.Keys(append); foreach (string ap in keys) { string sc = ap; if (symList.Contains(sc)) { continue; } symList.Add(sc); // There is no affiliation for weather int schemeKey = CodingScheme.GetCode(sc); if (schemeKey == CodingScheme.Weather) { // Check centering // var ls1 = new Line { X1 = x + 5, Y1 = y + 5, X2 = x - 5, Y2 = y - 5, Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 1 }; // var ls2 = new Line { X1 = x - 5, Y1 = y + 5, X2 = x + 5, Y2 = y - 5, Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 1 }; // cv.Children.Add(ls1); // cv.Children.Add(ls2); var ms = new MilSymbol(sc, Scale, "X=67.8"); DrawSymbol(cv, ms, x, y); if ((x += Tight) > 12 * Tight) { x = Edge; y += Tight; } continue; } if (schemeKey == CodingScheme.TacticalGraphics) { // Check centering // var ls1 = new Line { X1 = x + 5, Y1 = y + 5, X2 = x - 5, Y2 = y - 5, Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 1 }; // var ls2 = new Line { X1 = x - 5, Y1 = y + 5, X2 = x + 5, Y2 = y - 5, Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 1 }; // cv.Children.Add(ls1); // cv.Children.Add(ls2); sc = sc.Substring(0, 1) + "H" + sc.Substring(2, 1) + "A" + sc.Substring(4); var ms = new MilSymbol(sc, Scale, "H=HH;H1=H1;W=W;W1=W1;T=TT;N=N;X=XX;V=VV;C=CC;Y=YY;Q=-60.0"); DrawSymbol(cv, ms, x, y); if ((x += Tight) > 12 * Tight) { x = Edge; y += Tight + 25; } continue; } if (!framed) { sc = sc.Substring(0, 2) + "X" + sc.Substring(3); } foreach (string c in affiliations) { sc = sc[0] + c + sc[2] + "C" + sc.Substring(4, 10) + "E"; var ms = new MilSymbol(sc, Scale, null, null); if (first && ms.Empty) { continue; } first = false; DrawSymbol(cv, ms, x, y); if ((x += Tight) > 12 * Tight) { x = Edge; y += Tight; } } } }
/// <summary> /// A test to make sure that all of the supported labels go into the right /// places and do the right things. /// </summary> private static void PlotAllLabels() { // Example using Run: <Run Foreground='Purple'>KK</Run> var labels = new[] { "C=12345;F=±", "G=GG;H=HH", "J=JJ;K=KK", "L=LL;M=MM", "N=NN;P=PP", "Q=30.6;T=TT", "V=VV;W=WW", "X=XX;Y=YY", "Z=ZZ;AA=ABCDEF", // Can't use = as separator in this string because of Foreground='Green', etc. "C:12345;F:±;G:Staff comments;H:H;J:J;" + "K:<Run Foreground='Green' FontWeight='Bold' FontStyle='Italic'>KK</Run>;" + "L:L;M:M;N:N;P:P;Q:30.6;" + "T:T;V:VvV;W:WwW;X:X;Y:Y;Z:ZzZzZ;AA:ABCDEF" }; Canvas cv = GetCanvas(); if (cv == null) { return; } var saveScheme = MilBrush.ColorScheme; MilBrush.ColorScheme = ColorSchemeProperty.Medium; double x = Tight; double y = Loose; var baseCode = new[] { "IUAASRU---", "SUGAEVAL--", "IUUASRU---" }; var affiliations = new[] { "U", "F", "N", "H" }; var style = new Style(typeof(TextBlock)); style.Setters.Add(new Setter(TextBlock.FontFamilyProperty, new FontFamily("Times New Roman"))); style.Setters.Add(new Setter(TextBlock.FontSizeProperty, 84.0)); style.Setters.Add(new Setter(TextBlock.FontWeightProperty, FontWeights.Bold)); style.Setters.Add(new Setter(TextBlock.ForegroundProperty, new SolidColorBrush(Colors.Brown))); int count = 0; var unknowns = new[] { "U", "W", "P", "G" }; var friendlies = new[] { "F", "A", "D", "M", "J", "K" }; var hostiles = new[] { "H", "S" }; var neutrals = new[] { "N", "L" }; foreach (string bd in baseCode) { foreach (string ee in affiliations) { foreach (string m in labels) { string af = string.Empty; switch (ee) { case "U": af = unknowns[count % 4]; break; case "F": af = friendlies[count % 6]; break; case "H": af = hostiles[count % 2]; break; case "N": af = neutrals[count % 2]; break; } count++; string sc = bd.Substring(0, 1) + af + bd.Substring(2, 8) + "--" + "***"; var ms = new MilSymbol(sc, Scale, m, null, null, style); DrawSymbol(cv, ms, x, y); if ((x += Wide) > 5 * Wide) { x = Tight; y += Loose; } } } style = null; } MilBrush.ColorScheme = saveScheme; }
/// <summary> /// Plot a Miscellaneous collection of symbols to extend code coverage. /// </summary> private static void PlotMiscellany() { // Create a diagonal linear gradient with four stops. var linBrush = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(0, 1) }; linBrush.GradientStops.Add(CreateGradientStop(Color.FromArgb(255, 255, 255, 160), 0.0)); linBrush.GradientStops.Add(CreateGradientStop(Color.FromArgb(255, 255, 160, 160), 0.4)); linBrush.GradientStops.Add(CreateGradientStop(Color.FromArgb(255, 160, 160, 255), 0.6)); linBrush.GradientStops.Add(CreateGradientStop(Color.FromArgb(255, 160, 255, 160), 1.0)); #if WINDOWS_UWP var uri = new Uri("ms-appx:///Assets/IcelandFlag64.png", UriKind.Absolute); var bmi = new BitmapImage(uri); #else StreamResourceInfo sr = Application.GetResourceStream( new Uri("Resources/IcelandFlag64.png", UriKind.Relative)); if (sr == null) { Log.WriteMessage( LogLevel.Error, "Cannot find the IcelandFlag64.png resource. Should be in GraphicsTest/Resources."); return; } var bmi = new BitmapImage(); #endif #if SILVERLIGHT bmi.SetSource(sr.Stream); #elif !WINDOWS_UWP bmi.BeginInit(); bmi.StreamSource = sr.Stream; bmi.EndInit(); #endif var imageBrush = new ImageBrush { ImageSource = bmi, Opacity = 0.5, Stretch = Stretch.UniformToFill }; Canvas cv = GetCanvas(); if (cv == null) { return; } double x = 50; double y = 50; var affiliations = new[] { "U", "A", "N", "H" }; foreach (string ee in affiliations) { string sc = "I" + ee + "GPSRU----F***"; var ms = new MilSymbol(sc, Scale, null, new SolidColorBrush(Colors.Cyan), linBrush); DrawSymbol(cv, ms, x, y); MilBrush.ColorScheme = ColorSchemeProperty.Dark; ms = new MilSymbol(sc, Scale, null, null); DrawSymbol(cv, ms, x += 50, y); MilBrush.ColorScheme = ColorSchemeProperty.Medium; ms = new MilSymbol(sc, Scale, null, new SolidColorBrush(Colors.Orange)); DrawSymbol(cv, ms, x += 50, y); MilBrush.ColorScheme = ColorSchemeProperty.Light; ms = new MilSymbol(sc, Scale, null, null); DrawSymbol(cv, ms, x += 50, y); sc = "I" + ee + "GDSRU----J***"; ms = new MilSymbol(sc, Scale, null, null, linBrush); DrawSymbol(cv, ms, x += 50, y); sc = "I" + ee + "GXSRU----N***"; ms = new MilSymbol(sc, Scale, null, null, linBrush); DrawSymbol(cv, ms, x += 50, y); sc = "I" + ee + "ZPSRU----D***"; ms = new MilSymbol(sc, Scale, null, null, imageBrush); DrawSymbol(cv, ms, x += 50, y); sc = "I" + ee + "ZASRU----B**C"; ms = new MilSymbol(sc, Scale, null, new SolidColorBrush(Colors.Green)); // ReSharper disable RedundantAssignment DrawSymbol(cv, ms, x += 50, y); // ReSharper restore RedundantAssignment x = 50; y += 75; } // Cloud cover x = 0; for (int i = 0; i < 10; i++) { var ms = new MilSymbol("WAS-WC----P----", Scale, "AA=" + i); DrawSymbol(cv, ms, x += 50, y); } y += 75; // Wind barbs with no speeds x = 0; for (int i = 0; i < 10; i++) { var ms = new MilSymbol("WAS-WP----P----", 2.0 * Scale, "AA=" + i + ";Q=" + (i * 36)); DrawSymbol(cv, ms, x += 50, y); } y += 75; // Wind barbs with speeds x = 0; for (int i = 0; i < 10; i++) { string labels = "AA=" + i + ";Q=135;" + "Z=" + (i * 12.6); if (i > 5) { labels += ";Y=S"; } var ms = new MilSymbol("WAS-WP----P----", 2.0 * Scale, labels); DrawSymbol(cv, ms, x += 50, y); } y += 75; var brushes = new[] { new SolidColorBrush(Colors.Black), null, new SolidColorBrush(Colors.Magenta) }; x = 0; for (int i = 0; i < 3; i++) { var newScale = (i == 0) ? Scale : -Scale; var ms = new MilSymbol("WAS-WSTSD-P----", newScale, null, brushes[i], brushes[i]); DrawSymbol(cv, ms, x += 50, y); ms = new MilSymbol("WAS-WSTSS-P----", newScale, null, brushes[i], brushes[i]); DrawSymbol(cv, ms, x += 50, y); ms = new MilSymbol("WAS-WSTSH-P----", newScale, null, brushes[i], brushes[i]); DrawSymbol(cv, ms, x += 50, y); } var dms = new MilSymbol("WOS-HDS---P----", Scale, "X=67.8"); // ReSharper disable RedundantAssignment DrawSymbol(cv, dms, x += 50, y); // ReSharper restore RedundantAssignment }
/// <summary> /// Generates a PNG-based byte array representing a MilSymbol code. /// </summary> /// <param name="symbolIdCode"> /// The MilSymbol code. /// </param> /// <param name="scale"> /// The scale, typically a number between 0 and 1. /// </param> /// <param name="labelString"> /// The labels for the symbol. /// </param> /// <param name="opacity"> /// The opacity for the symbol. /// </param> /// <param name="lineBrush"> /// The line brush for coloring the symbol's outline. /// </param> /// <param name="fillBrush"> /// The fill brush for coloring the symbol's background. /// </param> /// <param name="storagePath"> /// The storage path for caching the PNG. /// </param> /// <param name="context"> /// The request/response context. /// </param> /// <returns> /// A boolean indicating whether we have a bitmap. /// </returns> public static bool GetSymbolBuffer( string symbolIdCode, double scale, string labelString, double opacity, Brush lineBrush, Brush fillBrush, string storagePath, HttpListenerContext context) { if (symbolIdCode.Length < 4 || symbolIdCode.Length > 15) { return(false); } if (symbolIdCode.Length < 15) { symbolIdCode += Padding.Substring(symbolIdCode.Length); } var symbol = new MilSymbol(symbolIdCode, scale, labelString, lineBrush, fillBrush); if (symbol.Bounds == Rect.Empty) { symbolIdCode = symbolIdCode.Substring(0, 4) + Padding.Substring(4); symbol = new MilSymbol(symbolIdCode, scale, labelString, lineBrush, fillBrush); if (symbol.Bounds == Rect.Empty) { return(false); } } // Create a bigger pixel map if we're doing labels var size = (labelString.Length > 0) ? (int)(scale * 2000d) : (int)(scale * 600d); symbol.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var halfSize = size / 2; symbol.Arrange(new Rect(halfSize, halfSize, size, size)); symbol.UpdateLayout(); symbol.Opacity = opacity; var bitmap = new RenderTargetBitmap(size, size, 96, 96, PixelFormats.Default); bitmap.Render(symbol); var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); // Cannot save the encoder output directly to the OutputStream - bummer using (var stream = new MemoryStream()) { encoder.Save(stream); using (var file = new FileStream(storagePath, FileMode.Create, FileAccess.Write)) { stream.WriteTo(file); } stream.WriteTo(context.Response.OutputStream); return(true); } }