// determines whether the button has been hit public bool IsHit(IInputModeContext context, PointD location) { // see if the button is visible at all IOrientedRectangle orientedRectangle = label.GetLayout(); if (orientedRectangle.Contains(location, context.HitTestRadius)) { location = location - orientedRectangle.GetAnchorLocation(); double upX = orientedRectangle.UpX; double upY = orientedRectangle.UpY; double tx = location.X * -upY + location.Y * upX; double ty = location.X * upX + location.Y * upY; // consider auto flipping of the label contents if (upY > 0) { return (new RectD(inset, inset, 20, orientedRectangle.Height - 2 * inset).Contains(new PointD(tx, ty), context.HitTestRadius)); } else { return (new RectD(orientedRectangle.Width - (inset + buttonWidth), inset, buttonWidth, orientedRectangle.Height - 2 * inset).Contains(new PointD(tx, ty), context.HitTestRadius)); } } else { return(false); } }
public static void Set(this OrientedRectangle mutable, IOrientedRectangle r) { mutable.AnchorX = r.AnchorX; mutable.AnchorY = r.AnchorY; mutable.Width = r.Width; mutable.Height = r.Height; mutable.UpX = r.UpX; mutable.UpY = r.UpY; }
/// <summary> /// Creates the geometry for the label's path. /// </summary> /// <param name="layout">The label's layout.</param> /// <param name="text">The label text.</param> /// <returns>The label path's geometry.</returns> /// <remarks> /// The label's visualization is an approximation of its text where each word is represented by a line with a length /// proportional to the word's length. In the vast majority of cases this approximates the position of spaces in the line /// accurately enough that the switch from a text label to a fast label is almost imperceptible. /// </remarks> private StreamGeometry CreateGeometry(IOrientedRectangle layout, string text) { var origin = layout.GetAnchorLocation(); var up = layout.GetUp(); var right = new PointD(-up.Y, up.X); if (AutoFlip && up.Y > 0) { origin = origin + up * layout.Height + right * layout.Width; up = -up; right = -right; } var upperLeft = origin + up * layout.Height; // This part could be optimized a bit by not allocating new objects and thus reduce pressure on the garbage // collector. However, in practice it made not much of an impact. var lines = text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var longestLineLength = lines.Max(l => l.Length); var sizePerLetter = layout.Width / longestLineLength; var geometry = new StreamGeometry(); using (var ctx = geometry.Open()) { // Fill – only drawn when a brush was set if (BackgroundBrush != null) { ctx.BeginFigure(origin, true, false); ctx.PolyLineTo(new Point[] { origin + right * layout.Width, origin + right * layout.Width + up * layout.Height, origin + up * layout.Height }, false, false); } // Lines for (int i = 0; i < lines.Length; i++) { var line = lines[i]; var words = line.Split(' ', ' '); var currentPoint = upperLeft - up * layout.Height / lines.Length * (i + 0.6); // Words foreach (var word in words) { var wordLength = word.Length * sizePerLetter; var targetPoint = currentPoint + right * wordLength; ctx.BeginFigure(currentPoint, false, false); ctx.LineTo(targetPoint, true, false); currentPoint = targetPoint + right * sizePerLetter; } } } return(geometry); }
// Create an ImageVisual which represents the button private static ImageVisual CreateButton(IOrientedRectangle layout) { var imageVisual = new ImageVisual { Image = Resources.edit_label, Rectangle = new RectD(layout.Width - (inset + buttonWidth), inset, buttonWidth, layout.Height - inset * 2) }; return(imageVisual); }
public ButtonVisual(ILabel label, Button button, ButtonIcon icon, Color backgroundColor, Color foregroundColor) { this.label = label; this.labelLayout = label.LayoutParameter.Model.GetGeometry(label, label.LayoutParameter); this.button = button; this.icon = icon; this.backgroundColor = backgroundColor; this.foregroundColor = foregroundColor; }
/// <summary> /// Helper method to determine whether two <see cref="IOrientedRectangle" />s are equal. /// </summary> /// <param name="rect1">The first rectangle to compare.</param> /// <param name="rect2">The second rectangle to compare.</param> /// <returns><see langword="true" /> if both rectangles are equal, <see langword="false" /> otherwise.</returns> /// <remarks> /// Two oriented rectangles are considered equal if their anchor location, up vector, width, and height are equal. /// </remarks> private static bool AreEqual(IOrientedRectangle rect1, IOrientedRectangle rect2) { // ReSharper disable CompareOfFloatsByEqualityOperator return(rect1.AnchorX == rect2.AnchorX || rect1.AnchorY == rect2.AnchorY || rect1.UpX == rect2.UpX || rect1.UpY == rect2.UpY || rect1.Width == rect2.Width || rect1.Height == rect2.Height); // ReSharper restore CompareOfFloatsByEqualityOperator }
public Visual CreateSelectionVisual(IRenderContext context, IOrientedRectangle layout) { var template = context.CanvasControl.TryFindResource(OrientedRectangleIndicatorInstaller.SelectionTemplateKey) as DataTemplate; var container = new VisualGroup(); var frameworkElement = template != null ? (template.LoadContent() as FrameworkElement) : null; if (frameworkElement != null) { container.Children.Add(frameworkElement); return(UpdateSelectionVisual(context, container, layout)); } return(null); }
/// <summary> /// Gets the location that is specified by the given ratios. /// </summary> private static PointD GetLocation(IOrientedRectangle rectangle, double ratioWidth, double ratioHeight) { var x1 = rectangle.AnchorX; var y1 = rectangle.AnchorY; var upX = rectangle.UpX; var upY = rectangle.UpY; var w = rectangle.Width * ratioWidth; var h = rectangle.Height * ratioHeight; var x2 = x1 + upX * h - upY * w; var y2 = y1 + upY * h + upX * w; return(new PointD(x2, y2)); }
ILabelModelParameter ILabelModelParameterFinder.FindBestParameter(ILabel label, ILabelModel model, IOrientedRectangle labelLayout) { var leftParam = ((ILabelModelParameterFinder)leftModel).FindBestParameter(label, leftModel, labelLayout); var rightParam = ((ILabelModelParameterFinder)rightModel).FindBestParameter(label, rightModel, labelLayout); var leftGeom = ((ILabelModel)leftModel).GetGeometry(label, leftParam); var rightGeom = ((ILabelModel)rightModel).GetGeometry(label, rightParam); var layoutCenter = labelLayout.GetCenter(); double leftDist = leftGeom.GetCenter().DistanceTo(layoutCenter); double rightDist = rightGeom.GetCenter().DistanceTo(layoutCenter); return(leftDist < rightDist ? new RotatedSideSliderParameter(leftParam, this) : new RotatedSideSliderParameter(rightParam, this)); }
/// <summary> /// Creates the geometry for the selection rectangle. /// </summary> /// <param name="layout">The label's layout.</param> /// <returns>The selection rectangle geometry.</returns> private Geometry CreateGeometry(IOrientedRectangle layout) { var up = layout.GetUp(); var anchor = layout.GetAnchorLocation(); var right = new PointD(-up.Y, up.X); var g = new StreamGeometry(); using (var ctx = g.Open()) { ctx.BeginFigure(anchor, Fill != null, true); ctx.LineTo(anchor + right * layout.Width, true, false); ctx.LineTo(anchor + right * layout.Width + up * layout.Height, true, false); ctx.LineTo(anchor + up * layout.Height, true, false); ctx.LineTo(anchor, true, false); } return(g); }
private static GeneralPath CreatePath(IOrientedRectangle layout) { GeneralPath gp = new GeneralPath(10); double xRad = layout.Width / 4; double yRad = layout.Height / 10; gp.MoveTo(0, yRad); gp.QuadTo(0, 0, xRad, 0); gp.LineTo(layout.Width - xRad, 0); gp.QuadTo(layout.Width, 0, layout.Width, yRad); gp.LineTo(layout.Width, layout.Height - yRad); gp.QuadTo(layout.Width, layout.Height, layout.Width - xRad, layout.Height); gp.LineTo(xRad, layout.Height); gp.QuadTo(0, layout.Height, 0, layout.Height - yRad); gp.Close(); return(gp); }
/// <summary> /// Returns the location of the specified position on the border of the oriented rectangle. /// </summary> private PointD GetLocation(IOrientedRectangle layout, HandlePositions position) { if (layout == null) { return(node.Layout.ToPointD()); } switch (position) { case HandlePositions.NorthWest: return(GetLocation(layout, 0.0, 1.0)); case HandlePositions.North: return(GetLocation(layout, 0.5, 1.0)); case HandlePositions.NorthEast: return(GetLocation(layout, 1.0, 1.0)); case HandlePositions.East: return(GetLocation(layout, 1.0, 0.5)); case HandlePositions.SouthEast: return(GetLocation(layout, 1.0, 0.0)); case HandlePositions.South: return(GetLocation(layout, 0.5, 0.0)); case HandlePositions.SouthWest: return(layout.GetAnchorLocation()); case HandlePositions.West: return(GetLocation(layout, 0.0, 0.5)); default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Tries to find a parameter that best matches the given layout for the /// provided label instance. /// </summary> /// <remarks>By default, this method is only called when <b>no discrete</b> candidates are specified (i.e. here for <see cref="CandidateCount"/> = 0. /// This implementation just calculates the rotation angle for the center of <paramref name="labelLayout"/> /// and creates a parameter for exactly this angle which <see cref="CreateParameter"/>.</remarks> ILabelModelParameter ILabelModelParameterFinder.FindBestParameter(ILabel label, ILabelModel model, IOrientedRectangle labelLayout) { var labelModel = model as MyNodeLabelModel; var node = label.Owner as INode; if (labelModel != null && node != null) { var direction = (labelLayout.GetCenter() - node.Layout.GetCenter()).Normalized; double ratio = Math.Atan2(direction.Y, -direction.X) / (Math.PI * 2.0d); return(labelModel.CreateParameter(ratio)); } else { return(DefaultLabelModelParameterFinder.Instance.FindBestParameter(label, model, labelLayout)); } }
/// <summary> /// Creates a new instance. /// </summary> public RotatedNodeShape(GeneralPath outline, IOrientedRectangle orientedLayout) { Outline = outline; OrientedLayout = orientedLayout; }
/// <summary> /// Creates the visual appearance of a label /// </summary> private void Render(IRenderContext context, ILabel label, VisualGroup container, IOrientedRectangle labelLayout, RenderDataCache cache) { // store information with the visual on how we created it container.SetRenderDataCache(cache); // background rectangle System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle { Width = labelLayout.Width, Height = labelLayout.Height, RadiusX = labelLayout.Width / 10, RadiusY = labelLayout.Height / 10, Stroke = Brushes.SkyBlue, Fill = fillBrush, StrokeThickness = 1 }; container.Add(rect); // TextBlock with label text TextBlock textBlock = new TextBlock { Text = cache.LabelText, FontFamily = cache.Typeface.FontFamily, FontStretch = cache.Typeface.Stretch, FontStyle = cache.Typeface.Style, FontWeight = cache.Typeface.Weight, Foreground = Brushes.Black, }; textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); double textPositionLeft = (labelLayout.Width - textBlock.DesiredSize.Width) / 2; textBlock.SetCanvasArrangeRect(new Rect(textPositionLeft, (labelLayout.Height - textBlock.DesiredSize.Height) / 2, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height )); container.Add(textBlock); }
/// <summary> /// Finds the label model parameter that describes the given label layout best. /// </summary> /// <remarks> /// Sometimes the layout cannot be met exactly, then the nearest location is used. /// </remarks> public ILabelModelParameter FindBestParameter(ILabel label, ILabelModel model, IOrientedRectangle labelLayout) { var wrapperModel = model as RotatableNodeLabelModelDecorator; var styleWrapper = wrapperModel.GetNodeStyleWrapper(label); if (!wrapperModel.UseNodeRotation || styleWrapper == null || styleWrapper.Angle == 0) { return (wrapperModel.CreateWrappingParameter(wrappedFinder.FindBestParameter(label, wrapperModel.Wrapped, labelLayout))); } var node = label.Owner as INode; var rotatedCenter = styleWrapper.GetRotatedPoint(labelLayout.GetCenter(), node, false); var rotatedLayout = styleWrapper.GetRotatedLayout(node); var rectangle = new OrientedRectangle(labelLayout); rectangle.Angle -= rotatedLayout.GetRadians(); rectangle.SetCenter(rotatedCenter); return (wrapperModel.CreateWrappingParameter(wrappedFinder.FindBestParameter(label, wrapperModel.Wrapped, rectangle))); }
/// <summary> /// Creates the visual appearance of a label /// </summary> private void Render(IRenderContext context, ILabel label, VisualGroup container, IOrientedRectangle labelLayout, RenderDataCache cache) { // store information with the visual on how we created it container.SetRenderDataCache(cache); // background rectangle System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle { Width = labelLayout.Width, Height = labelLayout.Height, RadiusX = labelLayout.Width / 10, RadiusY = labelLayout.Height / 10, Stroke = Brushes.SkyBlue, Fill = fillBrush, StrokeThickness = 1 }; container.Add(rect); // TextBlock with label text TextBlock textBlock = new TextBlock { Text = cache.LabelText, FontFamily = cache.Typeface.FontFamily, FontStretch = cache.Typeface.Stretch, FontStyle = cache.Typeface.Style, FontWeight = cache.Typeface.Weight, Foreground = Brushes.Black, }; textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); // if edit button is visible align left, otherwise center double textPositionLeft = cache.ButtonVisibility == Visibility.Visible ? HorizontalInset : (labelLayout.Width - textBlock.DesiredSize.Width) / 2; textBlock.SetCanvasArrangeRect(new Rect(textPositionLeft, (labelLayout.Height - textBlock.DesiredSize.Height) / 2, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height )); container.Add(textBlock); //////////////////////////////////////////////////// //////////////// New in this sample //////////////// //////////////////////////////////////////////////// if (cache.ButtonVisibility == Visibility.Visible) { // get style for edit button from XAML resources // create edit button Button editLabelButton = new Button { Style = editButtonStyle }; editLabelButton.SetCanvasArrangeRect(new Rect(labelLayout.Width - HorizontalInset - ButtonSize, VerticalInset, ButtonSize, ButtonSize)); // set button command editLabelButton.Command = GraphCommands.EditLabel; editLabelButton.CommandParameter = label; editLabelButton.CommandTarget = context.CanvasControl; container.Add(editLabelButton); } //////////////////////////////////////////////////// }
public Visual UpdateSelectionVisual(IRenderContext context, Visual oldVisual, IOrientedRectangle layout) { var container = oldVisual as VisualGroup; if (container != null && container.Children.Count == 1) { var visual = container.Children[0] as FrameworkElement; if (visual != null) { Transform transform = context.IntermediateTransform; container.Transform = transform; var anchor = layout.GetAnchorLocation(); var anchorAndUp = anchor + layout.GetUp(); anchor = context.WorldToIntermediateCoordinates(anchor); anchorAndUp = context.WorldToIntermediateCoordinates(anchorAndUp); var or = new OrientedRectangle(); or.SetUpVector((anchorAndUp - anchor).Normalized); or.SetAnchor(anchor); or.Width = layout.Width * context.Zoom; or.Height = layout.Height * context.Zoom; visual.Width = or.Width; visual.Height = or.Height; visual.SetCanvasArrangeRect(new Rect(0, 0, or.Width, or.Height)); ArrangeByLayout(context, visual, or, false); if (!container.IsMeasureValid) { container.Arrange(new Rect(0, 0, or.Width, or.Height)); } return(container); } } return(CreateSelectionVisual(context, layout)); }
public static SizeD ToSize(this IOrientedRectangle rect) { return(rect.ToSizeD()); }
/// <summary> /// Creates the visual appearance of a label /// </summary> private void Render(IRenderContext context, ILabel label, VisualGroup container, IOrientedRectangle labelLayout) { // background rectangle System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle { Width = labelLayout.Width, Height = labelLayout.Height, RadiusX = labelLayout.Width / 10, RadiusY = labelLayout.Height / 10, Stroke = Brushes.SkyBlue, Fill = fillBrush, StrokeThickness = 1 }; container.Add(rect); // TextBlock with label text TextBlock textBlock = new TextBlock { Text = label.Text, FontFamily = Typeface.FontFamily, FontStretch = Typeface.Stretch, FontStyle = Typeface.Style, FontWeight = Typeface.Weight, Foreground = Brushes.Black, }; textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); // if edit button is visible align left, otherwise center double textPositionLeft = (labelLayout.Width - textBlock.DesiredSize.Width) / 2; textBlock.SetCanvasArrangeRect(new Rect(textPositionLeft, (labelLayout.Height - textBlock.DesiredSize.Height) / 2, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height )); container.Add(textBlock); }
/// <summary> /// Creates the visual appearance of a label /// </summary> private void Render(IRenderContext context, ILabel label, VisualGroup container, IOrientedRectangle labelLayout, RenderDataCache cache) { // store information with the visual on how we created it container.SetRenderDataCache(cache); // background rectangle System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle { Width = labelLayout.Width, Height = labelLayout.Height, RadiusX = labelLayout.Width / 10, RadiusY = labelLayout.Height / 10, Stroke = Brushes.SkyBlue, Fill = fillBrush, StrokeThickness = 1 }; container.Add(rect); // TextBlock with label text TextBlock textBlock = new TextBlock { Text = cache.LabelText, FontFamily = cache.Typeface.FontFamily, FontStretch = cache.Typeface.Stretch, FontStyle = cache.Typeface.Style, FontWeight = cache.Typeface.Weight, Foreground = Brushes.Black, }; textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); // text is left aligned double textPositionLeft = HorizontalInset; textBlock.SetCanvasArrangeRect(new Rect(textPositionLeft, (labelLayout.Height - textBlock.DesiredSize.Height) / 2, textBlock.DesiredSize.Width, textBlock.DesiredSize.Height )); container.Add(textBlock); //////////////////////////////////////////////////// //////////////// New in this sample //////////////// //////////////////////////////////////////////////// // create edit button Button editLabelButton = new Button { Style = editButtonStyle }; editLabelButton.SetCanvasArrangeRect(new Rect(labelLayout.Width - HorizontalInset - ButtonSize, VerticalInset, ButtonSize, ButtonSize)); // let the button edit the label editLabelButton.Click += (sender, args) => ((GraphEditorInputMode)context.CanvasControl.InputMode).EditLabel(label); container.Add(editLabelButton); //////////////////////////////////////////////////// }