#define INTERNAL_MARKS using System; using System.IO; using System.Windows; using System.Windows.Media; using System.Diagnostics; using System.Globalization; using System.Windows.Controls; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using agree; namespace agree.Wpf.Util { using Math = System.Math; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public abstract class TfsDrawingVisual : DrawingVisual { protected TfsDrawingVisual(TfsVisualHost host) { this.host = host; } readonly public TfsVisualHost host; public Size m_size; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Visual element of a coreference box /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class TdeCoreference : TfsDrawingVisual { static Brush tag_brush = new SolidColorBrush(Colors.ForestGreen); static Pen tag_pen = new Pen(Brushes.ForestGreen, 1); int i_tag; public TdeCoreference(TfsVisualHost host, int i_tag) : base(host) { this.i_tag = i_tag; using (DrawingContext dc = RenderOpen()) Draw(dc); } void Draw(DrawingContext dc) { Double x = 0, y = 0; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; Double tag_height = font_size * .85; Rect r = new Rect(x, y, tag_height, tag_height); FormattedText ft = i_tag.ToString().FormattedText(font, tag_height * .9, tag_brush); r.Width = Math.Max(r.Width, ft.Width + 2); r.Width++; r.Height++; if (dc != null) { dc.DrawText(ft, x + ((r.Width / 2) - (ft.Width / 2)), y + ((r.Height / 2) - (ft.Height / 2))); dc.DrawRectangle(null, tag_pen, r); } m_size = new Size(r.Width, r.Height); } public int TagNumber { get { return i_tag; } } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Visual element of a Typed Feature Structure /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class TdeConstraint : TfsDrawingVisual { const Double pen_width = 1.5; const Double half_pen = pen_width / 2; static Pen black_pen = new Pen(Brushes.Black, pen_width); static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea)); static Brush mark_brush = new SolidColorBrush(Colors.Crimson); static Brush info_brush = new SolidColorBrush(Colors.Gray); static Brush hilt_brush = new SolidColorBrush(Color.FromArgb(0x80, 0xFF, 0xFE, 0x00)); static TdeConstraint() { black_pen.StartLineCap = PenLineCap.Square; black_pen.EndLineCap = PenLineCap.Square; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public TdeConstraint(TfsVisualHost host, Edge e) : base(host) { this.m_edge = e; if (host.tfs_highlight != null && host.tfs_highlight.Edge.Equals(e)) m_flags |= DrawState.Highlight; using (DrawingContext dc = RenderOpen()) Draw(dc); } Edge m_edge; public Edge Edge { get { return m_edge; } } [Flags] enum DrawState { Highlight = 0x01, OwnerBackground = 0x02, }; DrawState m_flags = 0; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Draw(DrawingContext dc) { Tfs tfs = host.owner._TfsEdge; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; Double line_height = font_size * 1.15; Double x = 0; Double y = 0; Double x_max = 0; if (host.owner.f_brackets) { x += 6; y += 2; } { Double x0 = x; FormattedText ft = tfs.tm.GetEdgeType(m_edge.FlagsId).Name.FormattedText(font, font_size, type_brush); if (dc != null) dc.DrawText(ft, x0, y); x0 += ft.Width + 4; if (host.viz == this) { String info = null; agree.Type ht = host.owner._TfsEdge.Type; if (ht.Definition.Edge.Equals(m_edge)) info = "definition"; else if (ht.Expanded.Edge.Equals(m_edge)) info = "expanded"; else info = "copy"; Typeface f2 = new Typeface(font.FontFamily, FontStyles.Italic, FontWeights.Normal, FontStretches.Normal); info = "(" + info + ")"; ft = info.FormattedText(f2, font_size, info_brush); if (dc != null) dc.DrawText(ft, x0, y); x0 += ft.Width + 2; } miew.Math.Extensions.Maximize(ref x_max, x0); y += line_height; } // get maximum width among feature strings Double feature_end_x = FeatureStringMax() + 3; // feature/type tuples foreach (ConstraintRef cref in tfs.AllConstraintRefs(m_edge).OrderBy(e => e.FeatureInfo.maximal_type.m_level)) { Size sz = DrawFeature(dc, cref, feature_end_x, x, y); if (x + sz.Width > x_max) x_max = x + sz.Width; y += sz.Height; } x = x_max; // feature structure outer brackets if (host.owner.f_brackets) { x += 4; y += 2; if (dc != null) { Double xhp = Math.Round(x) + half_pen; Double yhp = Math.Round(y) + half_pen; dc.DrawLine(black_pen, new Point(half_pen, half_pen), new Point(5 + half_pen, half_pen)); dc.DrawLine(black_pen, new Point(half_pen, half_pen), new Point(half_pen, yhp)); dc.DrawLine(black_pen, new Point(half_pen, yhp), new Point(5 + half_pen, yhp)); dc.DrawLine(black_pen, new Point(xhp - 5, half_pen), new Point(xhp, half_pen)); dc.DrawLine(black_pen, new Point(xhp, half_pen), new Point(xhp, y)); dc.DrawLine(black_pen, new Point(xhp - 5, yhp), new Point(xhp, yhp)); } x += pen_width + half_pen; y += pen_width + half_pen; } Size tsize = new Size(x, y); if (dc != null) { if (m_flags.HasFlag(DrawState.Highlight)) { //Rect rr = new Rect(VisualOffset.X, VisualOffset.Y, tsize.Width, tsize.Height); Rect rr = new Rect(0, 0, tsize.Width, tsize.Height); dc.DrawRectangle(hilt_brush, null, rr); } //else // if (m_flags.HasFlag(DrawState.Highlight)) // dc.DrawRectangle(host.owner.Background, null, new Rect(VisualOffset.X, VisualOffset.Y, m_size.Width, m_size.Height)); } m_size = tsize; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Double FeatureStringMax() { Tfs tfs = host.owner._TfsEdge; if (!tfs.tm.GetEdgeType(m_edge.FlagsId).HasAnyFeatures) return 0; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; return tfs.AllConstraintRefs(m_edge).Max(cref => { Double w = cref.Feature.ToUpper().FormattedText(font, font_size).Width; if (host.owner.f_marks) w += (" " + MarkText(cref.Constraint)).FormattedText(font, font_size * .85).Width; return w; }); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Size DrawFeature(DrawingContext dc, ConstraintRef cref, Double feature_end_x, Double x_base, Double y_base) { TypeMgr tm = host.owner._TfsEdge.tm; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; Double line_height = font_size * 1.15; FormattedText ft; Edge constraint = cref.Constraint; Double x = x_base; Double y = y_base; Brush b = tm.GetMaximalTypeForFeature(cref.i_feat) == tm.GetEdgeType(m_edge.FlagsId) ? Brushes.Black : Brushes.Gray; ft = cref.Feature.ToUpper().FormattedText(font, font_size, b); if (dc != null) dc.DrawText(ft, x, y); x += ft.Width; if (host.owner.f_marks #if INTERNAL_MARKS && !constraint.IsCoreferenced #endif ) { ft = (" " + MarkText(constraint)).FormattedText(font, font_size * .85, mark_brush); if (dc != null) dc.DrawText(ft, x, y + (font_size * .15)); x += ft.Width; } x = x_base + feature_end_x; bool f_coref_nodisplay = false; if (constraint.IsCoreferenced) { ReentrancyFinder.Entry ce; if (host.dict_corefs.TryGetValue(constraint,out ce) && (!cref.Equals(ce.cref_display) || cref.ConstraintTypeIsTop)) f_coref_nodisplay = true; } //if (cref.Pool.i_feat == tm.f_ix_dlist_list && !f_coref_nodisplay) //{ // y += 3; // TdeDiffList tv = new TdeDiffList(host, host.owner._TfsEdge.WrapEdgeInSameTfs(cref.Host)); // this.Children.Add(tv); // tv.Offset = new Vector(x, y); // x += tv.m_size.Width; // y += tv.m_size.Height; //} //else { ReentrancyFinder.Entry coref = null; if (constraint.IsCoreferenced) { Debug.Assert(host.dict_corefs != null); if (host.dict_corefs.TryGetValue(constraint, out coref)) { TdeCoreference tcr = #if INTERNAL_MARKS new TdeCoreference(host, constraint.Mark); #else new TdeCoreference(host, coref.i_tag); #endif this.Children.Add(tcr); Double yc = y + font_size * .20; tcr.Offset = new Vector(x, yc); x += tcr.m_size.Width + 5; } } #if true if (f_coref_nodisplay) { // don't render the type for coreference nodes when either: // a. it's been rendered elsewhere, or // b. it's *top* y += line_height; } else #endif { String val = tm.GetStringValue(cref.Constraint.FlagsId); if (val != null) { val = "“" + val + "”"; ft = val.FormattedText(font, font_size, type_brush); if (dc != null) dc.DrawText(ft, x, y); x += ft.Width + 2; y += line_height; } else if ((constraint.FlagsId & agree.Edge.Flag.PrunedDuringParsing) > 0) { ft = "(pruned during parsing)".FormattedText(font, font_size, mark_brush); if (dc != null) dc.DrawText(ft, x, y); x += ft.Width + 2; y += line_height; } else { Type t = tm.GetEdgeType(constraint.FlagsId); if (t.IsBare) { val = cref.ConstraintType.Name; ft = val.FormattedText(font, font_size, type_brush); if (dc != null) dc.DrawText(ft, x, y); x += ft.Width + 2; y += line_height; } else { y += 3; TdeConstraint tv = new TdeConstraint(host, constraint); this.Children.Add(tv); tv.Offset = new Vector(x, y); x += tv.m_size.Width; y += tv.m_size.Height; } } } } return new Size(x - x_base, y - y_base); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String MarkText(Edge e) { if (e.Mark == 0) return String.Empty; String s = e.IsCoreferenced ? "⇌" : String.Empty; return s + String.Format("{0}", e.Mark); } #if false protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) { Debug.WriteLine("viz.htc {0} {1}", this.Edge, hitTestParameters.HitPoint); return base.HitTestCore(hitTestParameters); } #endif }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class TdeDiffList : TfsDrawingVisual { static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea)); static Pen thin_black_pen = new Pen(Brushes.Black, 1); Tfs tfs; public TdeDiffList(TfsVisualHost host, Tfs te) : base(host) { this.tfs = te; using (DrawingContext dcx = RenderOpen()) Draw(dcx); } const double OPlusLRPad = 4; const double OPlusRadius = 6; void Draw(DrawingContext dc) { TypeMgr tm = host.owner._TfsEdge.tm; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; Double line_height = font_size * 1.15; Double x = 0; Double y_max = line_height; var rgrgcr = PartitionDiffList(tfs).ToArray(); int j; for (j = 0; j < rgrgcr.Length; j++) { ConstraintRef[] rgcr = rgrgcr[j]; Edge he = rgcr[0].Host; if (he.IsCoreferenced) { #if INTERNAL_MARKS TdeCoreference tcr = new TdeCoreference(host, he.Mark); #else ReentrancyFinder.Entry coref = host.dict_corefs[he]; TdeCoreference tcr = new TdeCoreference(host, coref.i_tag); #endif this.Children.Add(tcr); x += tcr.m_size.Width + 3; } TdeList tv = new TdeList(host, rgcr); this.Children.Add(tv); x += tv.m_size.Width + 2; /// OPlus goes here x += OPlusLRPad + (OPlusRadius * 2) + OPlusLRPad; y_max = Math.Max(y_max, tv.m_size.Height); } double y_center = y_max / 2; ////////////////////////////////////////////////////// /// second pass ////////////////////////////////////////////////////// x = 0; j = 0; foreach (TfsDrawingVisual viz in Children) { ConstraintRef[] rgcr = rgrgcr[j]; viz.Offset = new Vector(x, y_center - viz.m_size.Height / 2); x += viz.m_size.Width; if (viz is TdeCoreference) x += 3; if (viz is TdeList) { x += 2; if (j < rgrgcr.Length) { x += OPlusLRPad + OPlusRadius; dc.DrawEllipse(null, thin_black_pen, new Point(x, y_center), OPlusRadius, OPlusRadius); dc.DrawLine(thin_black_pen, new Point(x, y_center - OPlusRadius), new Point(x, y_center + OPlusRadius)); dc.DrawLine(thin_black_pen, new Point(x - OPlusRadius, y_center), new Point(x + OPlusRadius, y_center)); x += OPlusRadius + OPlusLRPad; j++; } } } Edge e = tfs.GetEdge(tfs.tm.f_ix_dlist_last, tfs.Edge.Mark); if (e.IsCoreferenced) { #if INTERNAL_MARKS TdeCoreference tcr = new TdeCoreference(host, e.Mark); #else ReentrancyFinder.Entry coref = host.dict_corefs[e]; TdeCoreference tcr = new TdeCoreference(host, coref.i_tag); #endif this.Children.Add(tcr); tcr.Offset = new Vector(x, y_center - tcr.m_size.Height / 2); x += tcr.m_size.Width + 3; } String val = tfs.tm.GetEdgeType(e.FlagsId).Name; FormattedText ft = val.FormattedText(font, font_size, type_brush); dc.DrawText(ft, x, y_center - ft.Height /2); x += ft.Width + 2; m_size = new Size(x, y_max); } static IEnumerable<ConstraintRef[]> PartitionDiffList(Tfs te) { var rgcr = te.GetDiffListLists(te.Edge).ToArray(); ConstraintRef[] sect; for (int i = 0; (sect = rgcr.Skip(i).TakeWhile((cr, ix) => ix == 0 || !cr.Host.IsCoreferenced).ToArray()).Length > 0; i += sect.Length) yield return sect; } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Retained graphics instructions for an AVM "list" /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class TdeList : TfsDrawingVisual { const Double pen_width = 1.5; const Double half_pen = pen_width / 2; static Pen black_pen = new Pen(Brushes.Black, pen_width); static Brush type_brush = new SolidColorBrush(Color.FromRgb(0x4e, 0x4e, 0xea)); IEnumerable<ConstraintRef> rgcr; public TdeList(TfsVisualHost host, IEnumerable<ConstraintRef> rgcr) : base(host) { this.rgcr = rgcr; using (DrawingContext dcx = RenderOpen()) Draw(dcx); } const Double AngleWidth = 7.0; const Double AngleYMargin = 10; void Draw(DrawingContext dc) { TypeMgr tm = host.owner._TfsEdge.tm; Typeface font = host.owner.TypeFace; Double font_size = host.owner.FontSize; Double line_height = font_size * 1.15; FormattedText comma = ",".FormattedText(font, 20.0); ////////////////////////////////////////////////////// /// first pass: create items and find item with the /// largest height ////////////////////////////////////////////////////// Double x = AngleWidth + 5; Double y_max = 0; bool f_first = true; foreach (ConstraintRef cr in rgcr) { if (f_first) f_first = false; else x += comma.Width + 5; TdeConstraint tv = new TdeConstraint(host, cr.Constraint); this.Children.Add(tv); y_max = Math.Max(y_max, tv.m_size.Height); x += tv.m_size.Width + 5; } ////////////////////////////////////////////////////// /// calculations based on tallest item ////////////////////////////////////////////////////// double y_center = y_max / 2; double angle_height = Math.Max(Math.Min(y_max - (AngleYMargin * 2), 180), 35); double y_margin = (y_max - angle_height) / 2; ////////////////////////////////////////////////////// /// second pass ////////////////////////////////////////////////////// /// Front angle '<' Point pt_apex = new Point(0, y_max / 2); dc.DrawLine(black_pen, pt_apex, new Point(AngleWidth, y_margin)); dc.DrawLine(black_pen, pt_apex, new Point(AngleWidth, y_max - y_margin)); /// center each item vertically x = AngleWidth + 5; f_first = true; foreach (TdeConstraint tv in Children.OfType<TdeConstraint>()) { if (f_first) f_first = false; else { dc.DrawText(comma, new Point(x, y_center - comma.Height / 2)); x += comma.Width + 5; } tv.Offset = new Vector(x, y_center - tv.m_size.Height / 2); x += tv.m_size.Width + 5; } /// Back angle '>' x += AngleWidth; pt_apex = new Point(x, y_max / 2); dc.DrawLine(black_pen, pt_apex, new Point(x - AngleWidth, y_margin)); dc.DrawLine(black_pen, pt_apex, new Point(x - AngleWidth, y_max - y_margin)); m_size = new Size(x, y_max); } }; }