/// <summary> /// Generate the top labels for the symbol. /// </summary> /// <param name="high"> /// The height at which to generate the labels. /// </param> /// <param name="symbolCode"> /// The symbol code for which to generate the labels. /// </param> /// <param name="labels"> /// The dictionary of labels to generate. /// </param> /// <param name="style"> /// The style with which to render the labels. /// </param> /// <returns> /// A TextBlock that represents the rendered labels. /// </returns> internal static TextBlock GenerateTop(double high, string symbolCode, IDictionary <string, string> labels, Style style) { if (!SymbolData.Check(ref symbolCode)) { return(null); } if (labels == null) { return(null); } var middle = new TextBlock { Style = style /*TopLabels*/ }; bool gotLine = false; ProcessLabels(labels, true, new[] { "C" }, middle, ref gotLine); var height = gotLine ? (double)middle.GetValue(TextBlock.LineHeightProperty) : 0.0; middle.FindTextExtent(); // This positions the top line just above the symbol, I think SetTopLeft(middle, -middle.Width / 2.0, high - height); return(middle); }
/// <summary> /// Generate the Joker, Faker, or "eXercise" character. /// </summary> /// <param name="symbolCode"> /// The symbol code for which to generate the large character string. /// </param> /// <param name="labels"> /// A dictionary of the labels to be drawn. /// </param> /// <param name="style"> /// The style to apply to the labels. /// </param> /// <returns> /// The TextBlock that incorporates the labels. /// </returns> internal static TextBlock GenerateJokerFakerExercise(string symbolCode, IDictionary <string, string> labels, Style style) { // Check for matching code char theChar = StandardIdentity.GetExerciseAmplifyingDescriptor(symbolCode); if (theChar == (char)0) { return(null); } #if WINDOWS_UWP string theString = theChar.ToString(); #else string theString = theChar.ToString(Culture); #endif if (labels != null && labels.ContainsKey("F")) { theString += " " + labels["F"]; } Rect r = SymbolData.GetBounds(symbolCode); var tb = new TextBlock { Style = style, // BigLabels, Text = theString }; tb.FindTextExtent(); tb.SetValue(Canvas.TopProperty, r.Top - tb.Height); tb.SetValue(Canvas.LeftProperty, r.Right); return(tb); }
/// <summary> /// Sets the width and height of the base symbol based on the symbol code, /// as well as the top and left properties, /// checking first to see if the canvas already has such limits. /// </summary> /// <param name="symbolCode"> /// The symbol code for the symbol. /// </param> private void SetLimits(string symbolCode) { this.SetValue(Canvas.TopProperty, 0.0); this.SetValue(Canvas.LeftProperty, 0.0); if (this.ApplyTemplate()) { if (VisualTreeHelper.GetChild(this, 0) is Canvas canvas && !double.IsNaN(canvas.Height)) { this.Height = canvas.Height; this.Width = canvas.Width; this.SetValue(Canvas.TopProperty, canvas.GetValue(Canvas.TopProperty)); this.SetValue(Canvas.LeftProperty, canvas.GetValue(Canvas.LeftProperty)); return; } } // Need to handle those symbols that still don't have bounds Rect rect = SymbolData.GetBounds(symbolCode); if (!rect.IsEmpty) { this.Height = rect.Height; this.Width = rect.Width; } else { this.Height = this.Width = 0.0; } }
/// <summary> /// Generate the headquarters rendering for the passed in symbol. /// </summary> /// <param name="ms"> /// The military symbol for which to provide the headquarters rendering. /// </param> /// <returns> /// The graphics Shape object that represents the headquarters rendering. /// </returns> private static Shape GenerateHeadquarters(MilSymbol ms) { Rect b = ms.BaseRect; double headquartersFactor = SymbolData.GetHqFactor(ms.SymbolCode); return(new Path { Style = SymbolData.GetStyle("BS10"), Data = new PathGeometry { Figures = new PathFigureCollection { new PathFigure { // The +/- HalfWidth accounts for the Bounds being on the outside of the symbol StartPoint = new Point(b.Left + SymbolData.HalfWidth, b.Top + (headquartersFactor * b.Height)), Segments = new PathSegmentCollection { new LineSegment { Point = new Point(b.Left + SymbolData.HalfWidth, b.Bottom + (0.3 * b.Width)) } } } } } }); }
/// <summary> /// Takes the symbol code and matches one of the stencils in the resource dictionary. /// </summary> /// <param name="symbolCode"> /// The symbol code for which to look up the stencil. /// </param> /// <returns> /// The string that should be used to get the ResourceDictionary entry for this symbol. /// </returns> public static string CodeToStencil(string symbolCode) { if (!SymbolData.Check(ref symbolCode)) { return(null); } int schemeKey = CodingScheme.GetCode(symbolCode); // Weather short circuits most considerations, at least for now if (schemeKey == CodingScheme.Weather) { return(symbolCode); } string stencil = symbolCode.Substring(0, 10).ToUpperInvariant(); int dash = stencil.IndexOf('-', 4); if (dash > 0) { stencil = stencil.Substring(0, dash); } // Replace remaining '-'s with '_'s stencil = stencil.Replace('-', '_'); // Return one of the four (approximately) that we know about int identityKey = StandardIdentity.GetNormalizedStandardIdentity(symbolCode); // Need the battle dimension int dimensionKey = CategoryBattleDimension.GetCode(symbolCode); // If we have a Z battle dimension cut right to the chase. // Get a blank stencil so we can add a question mark. if (dimensionKey == CategoryBattleDimension.BdUnknown) { return("I" + StandardIdentity.ToChar(identityKey) + "ZP"); } string tail = stencil.Substring(2, 1) + "P" + stencil.Substring(4); // If we have EmergenctManagement and a NaturalEvent, get rid of the standard identity // or if we have a tactical graphic, get rid of the standard identity if ((schemeKey == CodingScheme.EmergencyManagement && dimensionKey == CategoryBattleDimension.EmNaturalEvents) || schemeKey == CodingScheme.TacticalGraphics) { return(stencil.Substring(0, 1) + "_" + tail); } // If framed, we'll include the base affiliation if (dimensionKey != CategoryBattleDimension.BdOther) { return(stencil.Substring(0, 1) + StandardIdentity.ToChar(identityKey) + tail); } // Otherwise, we're looking for an unframed element return(stencil.Substring(0, 1) + "." + tail); }
/// <summary> /// Generate a TextBlock label based on an integer value. /// </summary> /// <param name="x"> /// The x coordinate for the TextBlock. /// </param> /// <param name="y"> /// The y coordinate for the TextBlock. /// </param> /// <param name="integerLabel"> /// The integer label to be displayed. /// </param> /// <param name="style"> /// The style for the label. /// </param> /// <returns> /// The TextBlock containing the integer as a label. /// </returns> internal static TextBlock IntegerLabel( double x, // a reference point for the horizontal location double y, // the top of the label - for now string integerLabel, // the label string style) // the label's style { var label = new TextBlock { Style = SymbolData.GetStyle(style) }; double result; if (!double.TryParse(integerLabel, out result)) { return(null); } if (result < 0.0 || result > 99999.0) { return(null); // negative or too many digits } integerLabel = Math.Round(result).ToString(Culture); label.Text = integerLabel; label.FindTextExtent(); var multiplier = (label.TextAlignment == TextAlignment.Center) ? 0.5 : (label.TextAlignment == TextAlignment.Right) ? 1.0 : 0.0; SetTopLeft(label, x - (multiplier * label.Width), y); return(label); }
/// <summary> /// Initializes static members of the <see cref="MilLabels"/> class. /// </summary> static MilLabels() { var styles = new Style[LabelCount]; styles[TopLabels] = SymbolData.GetStyle("TopLabels"); styles[BigLabels] = SymbolData.GetStyle("BigLabels"); styles[LeftLabels] = SymbolData.GetStyle("LeftLabels"); styles[RightLabels] = SymbolData.GetStyle("RightLabels"); styles[TacticalGraphicsLabels] = SymbolData.GetStyle("TacticalGraphicsLabels"); LabelStyles.Add(DefaultStyle, styles); }
/// <summary> /// Generate the feint dummy rendering for the passed in symbol. /// </summary> /// <param name="ms"> /// The military symbol for which to provide the feint dummy rendering. /// </param> /// <param name="height"> /// The height at which to provide the feint dummy rendering. /// </param> /// <returns> /// The graphics Path object that represents the feint dummy rendering. /// </returns> private static Path GenerateFeintDummy(MilSymbol ms, ref double height) { if (!string.IsNullOrEmpty(Echelon.GetEchelonSymbol(ms.SymbolCode))) { height = ms.Bounds.Top; } if (SymbolData.IsTopFlat(ms.SymbolCode)) { double minimum = ms.BaseRect.Top; if (height >= minimum) { height = minimum - 28; } } double bottom = height + 23; height = height - 27; const double Wide = 126; var st = new Style(typeof(Shape)) { BasedOn = SymbolData.GetStyle("BS10") }; st.SetDashArray(3, 1); return(new Path { Style = st, Data = new PathGeometry { Figures = new PathFigureCollection { new PathFigure { StartPoint = new Point(-Wide, bottom), Segments = new PathSegmentCollection { new LineSegment { Point = new Point(0.0, height) }, new LineSegment { Point = new Point(Wide, bottom) } } } } } }); }
/// <summary> /// Computes the scale factor used when scaling the symbol. /// </summary> /// <param name="symbolCode"> /// The symbol code whose scale factor we need. /// </param> private void SetScaleFactor(string symbolCode) { this.scaleFactor = 1.0; switch (ScaleType) { case ScaleTypeValues.EqualAreas: this.scaleFactor = 1.0 / SymbolData.GetScaling(symbolCode).AreaFactor; break; case ScaleTypeValues.EqualHeights: this.scaleFactor = 1.0 / SymbolData.GetScaling(symbolCode).LinearFactor; break; } }
/// <summary> /// Return the cache of styles for the particular symbol. /// </summary> /// <param name="style"> /// The style for which to retrieve the array of styles. /// </param> /// <returns> /// An array of styles, such as left labels, right labels, etc. that match the passed in style. /// </returns> internal static Style[] GetStyles(Style style) { if (style == null) { return(LabelStyles[DefaultStyle]); } if (LabelStyles.ContainsKey(style)) { return(LabelStyles[style]); } var styles = new Style[LabelCount]; styles[TopLabels] = new Style(typeof(TextBlock)) { BasedOn = SymbolData.GetStyle("TopLabels") }; styles[BigLabels] = new Style(typeof(TextBlock)) { BasedOn = SymbolData.GetStyle("BigLabels") }; styles[LeftLabels] = new Style(typeof(TextBlock)) { BasedOn = SymbolData.GetStyle("LeftLabels") }; styles[RightLabels] = new Style(typeof(TextBlock)) { BasedOn = SymbolData.GetStyle("RightLabels") }; styles[TacticalGraphicsLabels] = new Style(typeof(TextBlock)) { BasedOn = SymbolData.GetStyle("TacticalGraphicsLabels") }; var tb = new TextBlock { Style = style }; SetSingleProperty(TextBlock.ForegroundProperty, tb.Foreground, ref styles); SetSingleProperty(TextBlock.FontSizeProperty, tb.FontSize, ref styles); SetSingleProperty(TextBlock.FontFamilyProperty, tb.FontFamily, ref styles); SetSingleProperty(TextBlock.FontWeightProperty, tb.FontWeight, ref styles); LabelStyles.Add(style, styles); return(styles); }
/// <summary> /// This is the current background color for this particular symbol /// </summary> /// <param name="symbolCode">The symbol code</param> /// <returns>A brush representing the current background color for the symbol code</returns> public static Brush FindColorScheme(string symbolCode) { if (!SymbolData.Check(ref symbolCode)) { return(null); } if (OrderOfBattle.GetCode(symbolCode) == OrderOfBattle.Civilian) { if (StandardIdentity.IsColorHostile(symbolCode)) { return(GetDefault(Reds, ColorScheme)); } return(GetDefault(Purples, ColorScheme)); } switch (StandardIdentity.GetCode(symbolCode)) { case StandardIdentity.AssumedFriend: case StandardIdentity.ExerciseFriend: case StandardIdentity.Friend: case StandardIdentity.ExerciseAssumedFriend: return(GetDefault(Blues, ColorScheme)); case StandardIdentity.Hostile: case StandardIdentity.Joker: case StandardIdentity.Faker: case StandardIdentity.Suspect: return(GetDefault(Reds, ColorScheme)); case StandardIdentity.ExerciseNeutral: case StandardIdentity.Neutral: return(GetDefault(Greens, ColorScheme)); case StandardIdentity.ExercisePending: case StandardIdentity.Pending: case StandardIdentity.Unknown: case StandardIdentity.ExerciseUnknown: return(GetDefault(Yellows, ColorScheme)); default: return(null); } }
/// <summary> /// Returns the arrow representing a direction of travel for a mil symbol /// </summary> /// <param name="symbolCode">Code for the mil symbol</param> /// <param name="labels">The labels for the symbol, we're looking for "Q"</param> /// <param name="extraOffset">An extra offset for tactical graphics</param> /// <returns>The arrow shape representing the direction of travel</returns> internal static Shape GenerateQ( string symbolCode, IDictionary <string, string> labels, double extraOffset) { if (!SymbolData.Check(ref symbolCode)) { return(null); } string q; if (!labels.TryGetValue("Q", out q)) { return(null); } double angle; if (!double.TryParse(q, out angle)) { return(null); } double off = 0.0; if (CategoryBattleDimension.GetCode(symbolCode) == CategoryBattleDimension.BdGround) { off = SymbolData.GetBounds(symbolCode).Bottom; } off += extraOffset; return(new Path { Style = SymbolData.GetStyle("BS10"), Data = new PathGeometry { Figures = new PathFigureCollection { GenerateArrowPoints(off, angle) } } }); }
/// <summary> /// Generate the right labels for the symbol. /// </summary> /// <param name="symbolCode"> /// The symbol code for which to generate the labels. /// </param> /// <param name="labels"> /// The dictionary of labels to generate. /// </param> /// <param name="style"> /// The style with which to render the labels. /// </param> /// <returns> /// A TextBlock that represents the rendered labels. /// </returns> internal static TextBlock GenerateRight(string symbolCode, IDictionary <string, string> labels, Style style) { // Pick off the left-oriented strings and write them out if (labels == null) { return(null); } var right = new TextBlock { Style = style /*RightLabels*/ }; bool gotLine = false; // If we have an X, J, or K modifier character, skip label F because it is already written out. if (StandardIdentity.GetExerciseAmplifyingDescriptor(symbolCode) != (char)0) { ProcessLabels(labels, false, new[] { string.Empty }, right, ref gotLine); } else { ProcessLabels(labels, false, new[] { "F" }, right, ref gotLine); } var height = gotLine ? (double)right.GetValue(TextBlock.LineHeightProperty) : 0.0; // At this point we need to process the remaining labels as if each row has a label. // Otherwise some label lines may appear in the wrong position // if other label lines are empty. // Setting "gotLine" to true forces a new line for each of the label lines. // More complicated logic might consider the height of the text block, prior to // the elimination of the empty lines. gotLine = true; ProcessLabels(labels, false, new[] { "G" }, right, ref gotLine); ProcessLabels(labels, false, new[] { "H" }, right, ref gotLine); ProcessLabels(labels, false, new[] { "M" }, right, ref gotLine); ProcessLabels(labels, true, new[] { "J", "K", "L", "N", "P" }, right, ref gotLine); TruncateNewLines(right); Rect b = SymbolData.GetBounds(symbolCode); SetTopLeft(right, b.Right + (height / 5.0), b.Top - height); return(right); }
/// <summary> /// Generate the left labels for the symbol. /// </summary> /// <param name="symbolCode"> /// The symbol code for which to generate the labels. /// </param> /// <param name="labels"> /// The dictionary of labels to generate. /// </param> /// <param name="style"> /// The style with which to render the labels. /// </param> /// <returns> /// A TextBlock that represents the rendered labels. /// </returns> internal static TextBlock GenerateLeft(string symbolCode, IDictionary <string, string> labels, Style style) { if (!SymbolData.Check(ref symbolCode)) { return(null); } // Pick off the left-oriented strings and write them out if (labels == null) { return(null); } var left = new TextBlock { Style = style /*LeftLabels*/ }; bool gotLine = false; ProcessLabels(labels, false, new[] { "W" }, left, ref gotLine); var height = gotLine ? (double)left.GetValue(TextBlock.LineHeightProperty) : 0.0; // At this point we need to process the remaining labels as if each row has a label. // Otherwise some label lines may appear in the wrong position // if other label lines are empty. // Setting "gotLine" to true forces a new line for each of the label lines. // More complicated logic might consider the height of the text block, prior to // the elimination of the empty lines. gotLine = true; ProcessLabels(labels, false, new[] { "X", "Y" }, left, ref gotLine); ProcessLabels(labels, false, new[] { "V" }, left, ref gotLine); ProcessLabels(labels, false, new[] { "T" }, left, ref gotLine); ProcessLabels(labels, true, new[] { "Z" }, left, ref gotLine); TruncateNewLines(left); Rect b = SymbolData.GetBounds(symbolCode); left.FindTextExtent(); SetTopLeft(left, b.Left - left.Width - 10, b.Top - height); return(left); // we're only doing this to support unit test }
/// <summary> /// Generate the middle label for the symbol. /// </summary> /// <param name="symbolCode"> /// The symbol code for which to generate the labels. /// </param> /// <param name="labels"> /// The dictionary of labels to generate. /// </param> /// <returns> /// A TextBlock that represents the rendered labels. /// </returns> internal static TextBlock GenerateMiddle(string symbolCode, IDictionary <string, string> labels) { if (!SymbolData.Check(ref symbolCode)) { return(null); } if (labels == null) { return(null); } var middle = new TextBlock { Style = SymbolData.GetStyle("MiddleLabels") }; bool gotLine = false; ProcessLabels(labels, true, new[] { "AA" }, middle, ref gotLine); middle.FindTextExtent(); SetTopLeft(middle, -middle.Width / 2, -middle.Height / 2); return(middle); }
/// <summary> /// Initializes a new instance of the <see cref="MilSymbolBase"/> class. /// </summary> /// <param name="symbolCode"> /// The symbol code for this symbol. /// </param> /// <param name="lineBrush"> /// An optional line brush for outlining the symbol. /// </param> /// <param name="fillBrush"> /// An optional fill brush for filling the symbol's background. /// </param> public MilSymbolBase(string symbolCode, Brush lineBrush, Brush fillBrush) { if (symbolCode == null) { this.empty = true; return; } string stencil = CodeToStencil(symbolCode); if (MilAppendix.NoTemplate(stencil)) { this.empty = true; return; } this.DataContext = this; // Will need to treat weather and tactical graphics carefully int schemeKey = CodingScheme.GetCode(symbolCode); if (schemeKey == CodingScheme.Weather) { // These symbols can change color - so we bind to Line and Fill if (symbolCode.StartsWith("WAS-WSTS")) { if (lineBrush == null) { lineBrush = MilBrush.Rust; } if (fillBrush == null) { fillBrush = MilBrush.Rust; } this.SetLines(lineBrush); this.SetLineFills(lineBrush, fillBrush); } this.Template = SymbolData.GetControlTemplate(stencil); // gets the template - the main thing this.empty = this.Template == null; if (!this.empty) { this.SetLimits(symbolCode); } return; } // If the standard identity (StandardIdentity) is some type of pending, we'll need the // anticipated (dashhed) outline for the frame. this.needDashed = SymbolData.IsDashed(symbolCode); // There are occasions when we need a line style that matches affiliation and present int dimensionKey = CategoryBattleDimension.GetCode(symbolCode); bool needUnframed = schemeKey == CodingScheme.TacticalGraphics || dimensionKey == CategoryBattleDimension.BdOther || (schemeKey == CodingScheme.Warfighting && dimensionKey == CategoryBattleDimension.BdSubsurface); if (needUnframed) { this.unframedLine = MilBrush.GetLinePresent(MilBrush.FindColorScheme(symbolCode)); this.SetUnframedLines(lineBrush); } this.SetLines(lineBrush); // Get a brush style if user didn't specify if (fillBrush == null) { fillBrush = MilBrush.FindColorScheme(symbolCode); } if (needUnframed) { this.SetUnframedLineFills(fillBrush); } this.SetLineFills(lineBrush, fillBrush); this.Template = SymbolData.GetControlTemplate(stencil); // gets the template - the main thing this.empty = this.Template == null; if (!this.empty) { this.SetLimits(symbolCode); } }
/// <summary> /// Generate the installation rendering for the passed in symbol. /// </summary> /// <param name="ms"> /// The military symbol for which to provide the installation rendering. /// </param> /// <param name="height"> /// The height at which to provide the installation rendering. /// </param> /// <returns> /// The graphics Shape object that represents the installation rendering. /// </returns> private static Shape GenerateInstallation(MilSymbol ms, out double height) { //// U&PGW ZSGF full unknown (flat) //// AP full air (not flat) //// U subsurface (flat) //// N&L ZSGF full neutral (flat) //// AP full air (flat) //// U subsurface (flat) //// H&S ZSGF full hostile (not flat) //// AP full air (not flat) //// U subsurface (flat) //// F&ADMJK Z(IG)S full friendly (not flat) //// F(~IG) full friendly (flat) //// AP full air (not flat) //// U subsurface (flat) height = -185; Brush br = new SolidColorBrush(Colors.Black); int si = StandardIdentity.GetNormalizedStandardIdentity(ms.SymbolCode); if (SymbolData.IsTopFlat(ms.SymbolCode)) { double t = ms.BaseRect.Top; if (si == StandardIdentity.Unknown) { t += SymbolData.HalfWidth; } height = t - 30; return(GenericInstallation(br, height, t)); } int bd = CategoryBattleDimension.GetCode(ms.SymbolCode); bool ap = bd == CategoryBattleDimension.BdSpace || bd == CategoryBattleDimension.BdAir; // The cases cover those surfaces which are flat on top versus those that are not switch (si) { case StandardIdentity.Unknown: // Outside for unknown space if (ap) { return(UnknownSpaceInstallation(br, height)); } return(UnknownGroundInstallation(br, height)); case StandardIdentity.Hostile: // Outside for hostile space if (ap) { height = -165; return(HostileSpaceInstallation(br, height)); } return(HostileGroundInstallation(br, height)); case StandardIdentity.Friend: height = -175; // Outside for friendly space if (ap) { return(FriendSpaceInstallation(br, height)); } return(FriendCircleInstallation(br, height)); } return(null); }
/// <summary> /// Generates the correct combination of task force, installation, and feint dummy for a symbol. /// </summary> /// <param name="ms"> /// The symbol to which the generated rendering is attached. /// </param> /// <param name="symbolCode"> /// The symbol code for the given symbol /// </param> /// <returns> /// The maximum height of the generated rendering. /// </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 height = r.Top; if (!SymbolData.Check(ref symbolCode)) { return(height); } char code = ModifierCode.GetCode(symbolCode); switch (code) { case ModifierCode.Headquarters: // headquarters ms.AddChild("HQ", GenerateHeadquarters(ms)); break; case ModifierCode.TaskForceHeadquarters: // task force, headquarters ms.AddChild("TF", GenerateTaskForce(ms, out height)); ms.AddChild("HQ", GenerateHeadquarters(ms)); break; case ModifierCode.FeintDummyHeadquarters: // feint dummy, headquarters ms.AddChild("FD", GenerateFeintDummy(ms, ref height)); ms.AddChild("HQ", GenerateHeadquarters(ms)); break; case ModifierCode.FeintDummyTaskForceHeadquarters: // feint dummy, task force, headquarters ms.AddChild("TF", GenerateTaskForce(ms, out height)); ms.AddChild("FD", GenerateFeintDummy(ms, ref height)); ms.AddChild("HQ", GenerateHeadquarters(ms)); break; case ModifierCode.TaskForce: // task force ms.AddChild("TF", GenerateTaskForce(ms, out height)); break; case ModifierCode.FeintDummy: // feint dummy ms.AddChild("FD", GenerateFeintDummy(ms, ref height)); break; case ModifierCode.FeintDummyTaskForce: // feint dummy/task force ms.AddChild("TF", GenerateTaskForce(ms, out height)); ms.AddChild("FD", GenerateFeintDummy(ms, ref height)); break; case ModifierCode.Installation: // installation ms.AddChild("Installation", GenerateInstallation(ms, out height)); // There is an unfortunate overloading of the echelon character in the standard if (Echelon.GetCode(symbolCode) == 'B') { ms.AddChild("FD", GenerateFeintDummy(ms, ref height)); } break; case ModifierCode.Mobility: // mobility case ModifierCode.Towed: // towed break; } return(height); }
/// <summary> /// Generate the task force rendering for the passed in symbol. /// </summary> /// <param name="ms"> /// The military symbol for which to provide the task force rendering. /// </param> /// <param name="height"> /// The height at which to provide the task force rendering. /// </param> /// <returns> /// The graphics Path object that represents the task force rendering. /// </returns> private static Path GenerateTaskForce(MilSymbol ms, out double height) { height = -185; double bottom = 0; const double Wide = 54; int si = StandardIdentity.GetNormalizedStandardIdentity(ms.SymbolCode); if (SymbolData.IsTopFlat(ms.SymbolCode)) { bottom = ms.BaseRect.Top; if (si == StandardIdentity.Unknown) { bottom += SymbolData.HalfWidth; } height = bottom - 75; } else { int bd = CategoryBattleDimension.GetCode(ms.SymbolCode); bool ap = bd == CategoryBattleDimension.BdSpace || bd == CategoryBattleDimension.BdAir; // The cases cover those surfaces which are flat on top versus those that are not switch (si) { case StandardIdentity.Unknown: if (ap) { height = -230; // space bottom = -133; } else { height = -250; // ground bottom = -154; } break; case StandardIdentity.Hostile: if (ap) { height = -230; // space bottom = -107; } else { height = -253; // ground bottom = -119; } break; case StandardIdentity.Friend: height = -222; if (ap) { bottom = -121; // space } else { bottom = -137; // ground circles } break; } } return(new Path { Style = SymbolData.GetStyle("BS10"), Data = new PathGeometry { Figures = new PathFigureCollection { new PathFigure { StartPoint = new Point(Wide, bottom), Segments = new PathSegmentCollection { new LineSegment { Point = new Point(Wide, height) }, new LineSegment { Point = new Point(-Wide, height) }, new LineSegment { Point = new Point(-Wide, bottom) } } } } } }); }
/// <summary> /// Adds the black ribbon at the top of the symbol for the standard identity "Space". /// </summary> /// <param name="symbolCode">The symbol code for the space entity.</param> /// <returns>The shape representing the black ribbon overlay for a space entity.</returns> internal static Shape GenerateSpace(string symbolCode) { if (!SymbolData.Check(ref symbolCode)) { return(null); } if (CategoryBattleDimension.GetCode(symbolCode) != CategoryBattleDimension.BdSpace) { return(null); } // Handy points for future reference // Inside for unknown ground // <Path Data="F0 M-61,-140 C-30,-175 30,-175 61,-140" /> // Inside for hostile ground // <Polygon Points="-61,-105 0,-166 61,-105" /> switch (StandardIdentity.GetNormalizedStandardIdentity(symbolCode)) { case StandardIdentity.Friend: { return(GenerateBlackSpline( new Point(-61, -107), new Point(-23, -148), new Point(23, -148), new Point(61, -107))); } case StandardIdentity.Unknown: { return(GenerateBlackSpline( new Point(-61, -117), new Point(-30, -160), new Point(30, -160), new Point(61, -117))); } case StandardIdentity.Hostile: { return(new Path { Fill = new SolidColorBrush(Colors.Black), Data = new PathGeometry { Figures = new PathFigureCollection { new PathFigure { StartPoint = new Point(-61, -93), Segments = new PathSegmentCollection { new LineSegment { Point = new Point(0, -149) }, new LineSegment { Point = new Point(61, -93) } } } } } }); } case StandardIdentity.Neutral: { return(new Path { Fill = new SolidColorBrush(Colors.Black), Data = new PathGeometry { Figures = new PathFigureCollection { new PathFigure { StartPoint = new Point(-127, -104), Segments = new PathSegmentCollection { new LineSegment { Point = new Point(127, -104) }, new LineSegment { Point = new Point(127, -139) }, new LineSegment { Point = new Point(-127, -139) } } } } } }); } } return(null); }
/// <summary> /// Generate the labels for the single point weather symbology. /// </summary> /// <param name="symbolCode"> /// The weather symbol code. /// </param> /// <param name="labels"> /// The dictionary of labels to be displayed with the weather symbology. /// </param> /// <param name="addChild"> /// The delegate that will actually add the child to the UIElement. /// </param> internal static void GenerateWeather(string symbolCode, IDictionary <string, string> labels, AddChild addChild) { if (!SymbolData.Check(ref symbolCode)) { return; } if (labels == null) { return; } // There are very few weather codes that require labels - so we'll handle them via a case statement string label; switch (symbolCode) { case "WOS-HDS---P----": // X - altitude { double depth = 0.0; var ok = labels.ContainsKey("X") && double.TryParse(labels["X"], out depth); var splits = (ok ? depth.ToString(Culture) : "6.3").Split('.'); var meters = IntegerLabel(0, -100, splits[0], "MeterDepthLabel"); addChild("Meters", meters); // Plot the first fractional digit offset if (splits.Length > 1) { meters.FindTextExtent(); addChild( "Decimeters", IntegerLabel(meters.Width / 2, -25, splits[1].Substring(0, 1), "DecimeterDepthLabel")); } return; } case "WAS-WSF-LVP----": // X - altitude { label = labels.ContainsKey("X") ? labels["X"] : "100"; addChild("Altitude", IntegerLabel(63, -55, label, "FreezeLabel")); return; } case "WAS-WST-LVP----": // X - altitude { label = labels.ContainsKey("X") ? labels["X"] : "380"; addChild("Altitude", IntegerLabel(0, -61, label, "TropoLabel")); return; } case "WAS-PLT---P----": // low pressure { label = labels.ContainsKey("X") ? labels["X"] : "270"; addChild("Altitude", IntegerLabel(0, -136, label, "PressureLabel")); return; } case "WAS-PHT---P----": // high pressure { label = labels.ContainsKey("X") ? labels["X"] : "460"; addChild("Altitude", IntegerLabel(0, 2, label, "PressureLabel")); return; } case "WAS-WC----P----": { int cloudCover; // 0 through 9} if (labels.ContainsKey("AA") && int.TryParse(labels["AA"], out cloudCover)) { addChild("CloudCover", WindBarb.GenerateCloudCover(2, cloudCover)); } return; } case "WAS-WP----P----": // wind barb { int cloudCover; // 0 through 9} if (labels.ContainsKey("AA") && int.TryParse(labels["AA"], out cloudCover)) { addChild("CloudCover", WindBarb.GenerateCloudCover(1, cloudCover)); } double speed; // in knots double?speedIn = null; if (labels.ContainsKey("Z") && double.TryParse(labels["Z"], out speed)) { speedIn = speed; } bool southernHemisphere = labels.ContainsKey("Y") && labels["Y"].ToUpper() == "S"; double direction; // in degrees if (labels.ContainsKey("Q") && double.TryParse(labels["Q"], out direction)) { addChild("Wind", WindBarb.GenerateWind(speedIn, direction, southernHemisphere)); } return; } } }
/// <summary> /// Generate the labels for single point technical graphics. /// </summary> /// <param name="symbolCode"> /// The symbol code for the technical graphic. /// </param> /// <param name="labels"> /// The dictionary of labels for the technical graphic. /// </param> /// <param name="style"> /// The style for the labels. /// </param> /// <param name="addChild"> /// The delegate that actually adds the label to the UIElement. /// </param> internal static void GenerateTacticalGraphics( string symbolCode, IDictionary <string, string> labels, Style style, AddChild addChild) { if (!SymbolData.Check(ref symbolCode)) { return; } if (labels == null) { return; } string stencil = MilSymbolBase.CodeToStencil(symbolCode); double extraOffset; TextBlock tb; // These are special case codes that require labels switch (stencil) { case "G_MPNDP": case "G_MPNDA": case "G_MPNDT": case "G_MPNDE": case "G_MPNDB": case "G_MPNDO": case "G_MPNDD": case "G_FPPCS": case "G_FPPCB": case "G_FPPCR": case "G_FPPCH": case "G_FPPCL": TacticalGraphicsPointLabels(labels, addChild, new[] { "H", "N", "T", "W" }, style); break; case "G_GPGPP": TacticalGraphicsPointLabels(labels, addChild, new[] { "H", "H1", "N", "T", "W-" }, style); break; case "G_GPGPPK": case "G_GPGPPL": case "G_GPGPPP": case "G_GPGPPR": case "G_GPGPPE": case "G_GPGPPS": case "G_GPGPPA": case "G_GPOPP": case "G_MPBCP": case "G_SPPX": case "G_SPPC": case "G_SPPY": case "G_SPPT": case "G_SPPD": case "G_SPPE": case "G_SPPL": case "G_SPPM": case "G_SPPR": case "G_SPPU": case "G_SPPO": case "G_SPPI": case "G_SPPN": case "G_SPPSZ": case "G_SPPSA": case "G_SPPSB": case "G_SPPSC": case "G_SPPSD": case "G_SPPSE": case "G_SPPSF": case "G_SPPSG": case "G_SPPSH": case "G_SPPSI": case "G_SPPSJ": case "G_SPPAS": case "G_SPPAT": TacticalGraphicsPointLabels(labels, addChild, new[] { "H", "N", "T", "W-" }, style); break; case "G_GPGPRI": GenerateSingleLabel(labels, "T", 0, -246, 0.5, 0.5, style, addChild); break; case "G_GPGPH": GenerateSingleLabel(labels, "H", 0, 0, 0.5, 0.5, style, addChild); break; case "G_GPGPPC": GenerateSingleLabel(labels, "T", 0, -175, 0.5, 0.5, style, addChild); break; case "G_GPGPPD": GenerateSingleLabel(labels, "T", 0, 0, 0.5, 0.25, style, addChild); break; case "G_GPGPPW": GenerateSingleLabel(labels, "T", 50, 0, 0.0, 0.5, style, addChild); break; case "G_GPAPP": case "G_GPAPC": GenerateSingleLabel(labels, "T", 0, 0, 0.5, 0.25, style, addChild); break; case "G_GPDPT": GenerateSingleLabel(labels, "T", 87, -87, 0.5, 0.5, style, addChild); break; case "G_MPOHTL": GenerateSingleLabel(labels, "X", 50, -250, 0.0, 0.0, style, addChild); break; case "G_MPOHTH": GenerateSingleLabel(labels, "X", 50, -230, 0.0, 0.0, style, addChild); break; case "G_MPNZ": GenerateSingleLabel(labels, "W", -110, -300, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "V", -110, -200, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "T", -110, -100, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "H", 110, -300, 0.0, 0.0, style, addChild); GenerateSingleLabel(labels, "N", 110, -100, 0.0, 0.0, style, addChild); GenerateSingleLabel(labels, "C", 0, -300, 0.5, 1.0, style, addChild); tb = GenerateSingleLabel(labels, "Y", 0, 0, 0.5, 0.0, style, addChild); tb.FindTextExtent(); extraOffset = tb != null ? tb.Height : 0.1; addChild("Q", GenerateQ(symbolCode, labels, extraOffset)); break; case "G_MPNEB": GenerateSingleLabel(labels, "W", -110, -300, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "T", -110, -100, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "H", 110, -300, 0.0, 0.0, style, addChild); GenerateSingleLabel(labels, "N", 110, -100, 0.0, 0.0, style, addChild); tb = GenerateSingleLabel(labels, "Y", 0, 0, 0.5, 0.0, style, addChild); tb.FindTextExtent(); extraOffset = tb != null ? tb.Height : 0.1; addChild("Q", GenerateQ(symbolCode, labels, extraOffset)); break; case "G_MPNEC": GenerateSingleLabel(labels, "W", -94, -300, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "T", -94, -100, 1.0, 0.0, style, addChild); GenerateSingleLabel(labels, "H", 94, -300, 0.0, 0.0, style, addChild); GenerateSingleLabel(labels, "N", 94, -100, 0.0, 0.0, style, addChild); tb = GenerateSingleLabel(labels, "Y", 0, 0, 0.5, 0.0, style, addChild); tb.FindTextExtent(); extraOffset = tb != null ? tb.Height : 0.1; addChild("Q", GenerateQ(symbolCode, labels, extraOffset)); break; case "G_FPPTS": GenerateSingleLabel(labels, "H", 50, 75, 0.0, 0.5, style, addChild); GenerateSingleLabel(labels, "H1", -50, 75, 1.0, 0.5, style, addChild); GenerateSingleLabel(labels, "T", 50, -75, 0.0, 0.5, style, addChild); break; case "G_FPPTN": GenerateSingleLabel(labels, "T", 50, -75, 0.0, 0.5, style, addChild); break; case "G_FPPCF": GenerateSingleLabel(labels, "T", 50, 0, 0.0, 0.5, style, addChild); break; } }
/// <summary> /// Generates barge mobility object. /// </summary> /// <param name="symbolCode"> /// The symbol code containing the mobility indicator for which to create a mobility Shape. /// </param> /// <returns> /// A Shape object representing the mobility object. /// </returns> public static Shape GenerateMobility(string symbolCode) { if (!SymbolData.Check(ref symbolCode)) { return(null); } if (!Mobility.IsMobility(symbolCode)) { return(null); } Rect b = SymbolData.GetBounds(symbolCode); double bottom = b.Bottom + HalfWidth; bool flat = SymbolData.IsBaseFlat(symbolCode); const double X = End - Rad; switch (Mobility.GetCode(symbolCode)) { case Mobility.WheeledLimited: return(GenerateWheels(new[] { -X, X }, bottom)); case Mobility.WheeledCrossCountry: return(GenerateWheels(new[] { -X, 0, X }, bottom)); case Mobility.Tracked: return(GenerateTracked(bottom)); case Mobility.WheeledTracked: return(GenerateWheeledTracked(bottom)); case Mobility.Towed: if (flat) { bottom += Rad; } return(GenerateTowed(bottom)); case Mobility.Railway: return(GenerateWheels(new[] { -X, -X + TwoRad, X - TwoRad, X }, bottom)); case Mobility.OverSnow: if (flat) { bottom += TwoRad; } return(GenerateOverSnow(bottom)); case Mobility.Sled: if (!flat) { bottom -= TwoRad; } return(GenerateSled(bottom)); case Mobility.PackAnimals: return(GeneratePackAnimals(bottom)); case Mobility.Barge: return(GenerateBarge(bottom)); case Mobility.Amphibious: return(GenerateAmphibious(bottom)); case Mobility.TowedArrayShort: return(GenerateArray(new[] { -End - TwoSqr, 0.0, End + TwoSqr }, bottom + ThreeSqr - HalfWidth)); case Mobility.TowedArrayLong: if (!flat) { bottom -= Sqr + HalfWidth; } return(GenerateArray( new[] { -End - TwoSqr, -HalfEnd - Sqr, 0.0, HalfEnd + Sqr, End + TwoSqr }, bottom + ThreeSqr - HalfWidth)); } return(null); }