зеркало из https://github.com/stride3d/GraphX.git
Many changes. See changelog.txt
This commit is contained in:
Родитель
e67a8b42fb
Коммит
64163c710a
|
@ -1,13 +1,34 @@
|
|||
RELEASE 2.1.9
|
||||
RELEASE 2.1.9 WIP
|
||||
NEW TERMS:
|
||||
VAESPS - vertex and edge skip processing support
|
||||
VCP - vertex connection point
|
||||
|
||||
HIGHLIGHTS:
|
||||
I. As new edge pointers logic has been added (image/path based pointers) the old logic that utilizes direct Path object in EdgeControl template will be removed in GraphX 2.2.0
|
||||
II. VCPs has been added into GraphX making edge endpoint fixation(binding) possible. By default GraphX automaticaly calculates edge endpoint position relative to vertex dimensions
|
||||
and approximate math shape, but with this feature on you'll be able to bind edge endpoint to a separate customizable object. This will allow you to create static edge fixation
|
||||
endpoints on the vertex and give you full control over edge endpoints positioning.
|
||||
III. Now you can easily customize edge pointers with the help of EdgePointerImage and EdgePointerPath classes. Changes include more precious edge endpoint calculations so the edge
|
||||
will overlap with the edge pointer no more.
|
||||
|
||||
DETAILED CHANGELOG:
|
||||
+ Added support for custom VCP [WPF, METRO]
|
||||
* New control class added: StaticVertexConnectionPoint
|
||||
* VCP support the same math shapes as VertexShape property or can use VertexShape::None value to disable roundtrip calculations
|
||||
+ Added vertex and edge skip processing support to METRO version (VAESPS) [METRO]
|
||||
+ Added VAESPS for following algorithms: Circular [ALL]
|
||||
+ Added VertexShape::None enum option, currently affecting only VCP logic [ALL]
|
||||
+ Added different layout options to EfficientSugiyama algorithm using new Direction param [ALL]
|
||||
+ Added true orthogonal edge routing for EfficientSugiyama algorithm using EdgeRouting param [ALL]
|
||||
+ Refactored almost all of the GraphX code for improvements and code quality. Changed many namespaces for better name reflection [ALL]
|
||||
+ Fixed layout algorithm calculations to always receive actual vertex positions as input parameter
|
||||
This will fix VAESPS for default algorithms. [WPF, METRO]
|
||||
+ Fixed outdated edge rendering in some cases when vertex coordinates are changed manualy [WPF, METRO]
|
||||
+ Fixed edge overlapping edge pointers (image, path) [WPF, METRO]
|
||||
+ Adjusted FR and BoundedFR default values for random initial positions [All]
|
||||
+ Fixed some algorithm calculation problems, especial FR [ALL]
|
||||
|
||||
!BREAKING CHANGES
|
||||
BREAKING CHANGES:
|
||||
!!!WARNING!!! This GraphX version has fallen under the heavy hand of code refactoring and along with the numerous code improvements
|
||||
almost ALL of the namespaces has been changed to better reflect PCL and multiplatform library versioning. Also i've get rid of the
|
||||
several different XAML xmlns usings in favor of single one that reflects library platform. I'm very sorry that this changes will
|
||||
|
@ -19,6 +40,7 @@
|
|||
- All base models and interfaces are now in GraphX.PCL.Common.Models namespace
|
||||
- All GraphX primitives (Point, Rect, etc.) are left in GraphX.Measure for better code readability in cases when they are intersected with System.Windows namespace twins
|
||||
|
||||
IGXLogicCore now include ExternalLayoutAlgorithm, ExternalOverlapRemovalAlgorithm, ExternalEdgeRoutingAlgorithm for simplier external algo assignment
|
||||
|
||||
RELEASE 2.1.8
|
||||
+ Added basic support for Image based edge pointers. Introduced new object for EdgeControl template: [WPF, METRO(bugged)]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
New things:
|
||||
1. Sizes logic. Now matters only GraphArea::ContentSize
|
||||
2. Now all vertices and edges positioned in GraphArea using TOP-LEFT corner as the position coordinates (previously coordinates was pointing at the object center)
|
||||
TODO
|
||||
1. CUT source ep edge also
|
||||
|
||||
|
||||
|
||||
Add edge labels - labels on path
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
x:Class="METRO.SimpleGraph.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:METRO.SimpleGraph">
|
||||
>
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls1="using:GraphX.METRO.Controls">
|
||||
|
||||
<!-- REGION ZoomControl -->
|
||||
|
||||
<SolidColorBrush x:Key="SelectionBrush" Color="LightBlue" Opacity="0.5"/>
|
||||
<SolidColorBrush x:Key="AreaSelectionBrush" Color="LightGreen" Opacity="0.5"/>
|
||||
|
||||
<!-- REGION Misc controls -->
|
||||
<Style x:Key="TransparentButton" TargetType="ButtonBase"
|
||||
BasedOn="{x:Null}">
|
||||
<Setter Property="Background" Value="White" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ButtonBase">
|
||||
<Grid Background="Transparent" Name="ContainerGrid" >
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundTextBoxRectangle">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value=".5"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="content">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition To="PointerOver" GeneratedDuration="0:0:0.5"/>
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Rectangle Fill="{TemplateBinding Background}"
|
||||
Stroke="{TemplateBinding Foreground}" RadiusX="3" RadiusY="3"
|
||||
x:Name="BackgroundTextBoxRectangle" Opacity="0" />
|
||||
<ContentPresenter x:Name="content" Opacity=".5" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ViewFinderToggleButtonStyle" BasedOn="{StaticResource TransparentButton}" TargetType="ToggleButton">
|
||||
</Style>
|
||||
|
||||
<!-- ENDREGION -->
|
||||
<controls1:VisibilityToBoolConverter x:Key="TrueIfVisibleConverter"
|
||||
Inverted="False" Not="False" />
|
||||
<controls1:VisibilityToBoolConverter x:Key="TrueIfNotVisibleConverter"
|
||||
Inverted="False" Not="True" />
|
||||
<controls1:VisibilityToBoolConverter x:Key="VisibleIfTrueConverter"
|
||||
Inverted="True" Not="False" />
|
||||
<controls1:VisibilityToBoolConverter x:Key="VisibleIfNotTrueConverter"
|
||||
Inverted="True" Not="True" />
|
||||
|
||||
|
||||
|
||||
<Style TargetType="controls1:ZoomControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls1:ZoomControl">
|
||||
<Grid>
|
||||
|
||||
|
||||
<Border BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Background="{TemplateBinding Background}">
|
||||
<controls1:ZCP x:Name="PART_Presenter" />
|
||||
</Border>
|
||||
|
||||
<Border Background="Gray" BorderBrush="White" BorderThickness="4" CornerRadius="5" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5" Opacity=".8">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Button x:Name="FillButton" IsTabStop="False" Margin="5" Height="64" Width="64" Style="{StaticResource TransparentButton}" Command="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ZoomToFillCommand}" >
|
||||
<Image Stretch="Fill" Source="GraphX.METRO.Controls/Images/round2.png" ToolTipService.ToolTip="Zoom to fill" />
|
||||
</Button>
|
||||
<Button x:Name="CenterButton" IsTabStop="False" Margin="5" Height="64" Width="64" Style="{StaticResource TransparentButton}" Command="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CenterToContentCommand}" >
|
||||
<Image Stretch="Fill" Source="GraphX.METRO.Controls/Images/round1.png" ToolTipService.ToolTip="Center content" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
|
||||
<Setter Property="ZoomBoxBackground">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0.0, 0.0" EndPoint="1.0, 1.0" >
|
||||
<GradientStop Color="Silver" Offset="0.0" />
|
||||
<GradientStop Color="DarkGray" Offset="1.0" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ZoomBoxBorderBrush"
|
||||
Value="Black" />
|
||||
<Setter Property="ZoomBoxBorderThickness"
|
||||
Value="1" />
|
||||
|
||||
|
||||
</Style>
|
||||
|
||||
<!-- ENDREGION -->
|
||||
|
||||
<!-- REGION VERTEX CONTROL -->
|
||||
<Style TargetType="controls1:VertexControl">
|
||||
<Setter Property="Background" Value="#FFE3E3E3"/>
|
||||
<Setter Property="BorderThickness" Value="5,3,5,3"/>
|
||||
<Setter Property="Padding" Value="10,5,10,5"/>
|
||||
<Setter Property="BorderBrush" Value="#FF393939"/>
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls1:VertexControl">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
CornerRadius="10,10,10,10"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentPresenter Content="{TemplateBinding Vertex}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!-- ENDREGION -->
|
||||
|
||||
<!-- REGION EDGE CONTROL -->
|
||||
<Style TargetType="controls1:EdgeControl">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls1:EdgeControl">
|
||||
<Grid>
|
||||
<Path Stroke="{TemplateBinding Foreground}"
|
||||
StrokeThickness="2" MinWidth="1" MinHeight="1"
|
||||
x:Name="PART_edgePath"/>
|
||||
|
||||
<controls1:EdgePointerPath x:Name="PART_EdgePointerForTarget" Scale="8,8" Opacity="239">
|
||||
<controls1:EdgePointerPath.Path>
|
||||
<Path Data="M0,0.5 L1,1 1,0Z" Fill="Yellow"/>
|
||||
</controls1:EdgePointerPath.Path>
|
||||
</controls1:EdgePointerPath>
|
||||
<controls1:EdgeLabelControl x:Name="PART_edgeLabel" Content="{Binding Edge.Text, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
<Setter Property="LabelVerticalOffset" Value="0"/>
|
||||
<Setter Property="MinWidth"
|
||||
Value="1" />
|
||||
<Setter Property="MinHeight"
|
||||
Value="1" />
|
||||
<Setter Property="Background"
|
||||
Value="Red" />
|
||||
<Setter Property="Foreground"
|
||||
Value="Silver" />
|
||||
<Setter Property="Opacity"
|
||||
Value="1" />
|
||||
</Style>
|
||||
<!-- ENDREGION -->
|
||||
|
||||
<!-- REGION LABLE CONTROLS -->
|
||||
<Style TargetType="controls1:EdgeLabelControl">
|
||||
<Setter Property="DisplayForSelfLoopedEdges" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls1:EdgeLabelControl">
|
||||
<Grid>
|
||||
<Border BorderBrush="Black" BorderThickness="1" Background="LightCoral" CornerRadius="8">
|
||||
<ContentPresenter Margin="3"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
|
||||
</Style>
|
||||
|
||||
<!--VERTEX LABEL CONTROL -->
|
||||
<Style TargetType="controls1:VertexLabelControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="LabelPositionMode" Value="Sides"/>
|
||||
<Setter Property="LabelPositionSide" Value="BottomRight"/>
|
||||
|
||||
<!-- Custom label template body -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls1:VertexLabelControl">
|
||||
<Grid>
|
||||
<ContentPresenter Margin="3" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!-- ENDREGION -->
|
||||
|
||||
</ResourceDictionary>
|
|
@ -111,22 +111,23 @@
|
|||
<SolidColorBrush Color="{Binding VisualColor}"/>
|
||||
</Path.Stroke>
|
||||
</Path>
|
||||
<Path x:Name="PART_edgeArrowPath"
|
||||
Stroke="{TemplateBinding Foreground}"
|
||||
StrokeThickness="{Binding VisualEdgeThickness}"
|
||||
Opacity="{Binding VisualEdgeTransparency}"
|
||||
/>
|
||||
|
||||
<!--<controls:EdgePointerImage NeedRotation="true" x:Name="PART_EdgePointerForSource" >
|
||||
<controls:EdgePointerImage.Image>
|
||||
<controls1:EdgePointerPath x:Name="PART_EdgePointerForTarget" Opacity="239" Scale="8,8">
|
||||
<controls1:EdgePointerPath.Path>
|
||||
<Path Data="M0,0.5 L1,1 1,0" Fill="Yellow" />
|
||||
</controls1:EdgePointerPath.Path>
|
||||
</controls1:EdgePointerPath>
|
||||
|
||||
<!-- <controls1:EdgePointerImage NeedRotation="true" x:Name="PART_EdgePointerForSource" >
|
||||
<controls1:EdgePointerImage.Image>
|
||||
<Image Source="/Assets/tr_red.png" Width="15" Height="15"/>
|
||||
</controls:EdgePointerImage.Image>
|
||||
</controls:EdgePointerImage>
|
||||
<controls:EdgePointerImage NeedRotation="true" x:Name="PART_EdgePointerForTarget" >
|
||||
<controls:EdgePointerImage.Image>
|
||||
</controls1:EdgePointerImage.Image>
|
||||
</controls1:EdgePointerImage>
|
||||
<controls1:EdgePointerImage NeedRotation="true" x:Name="PART_EdgePointerForTarget" >
|
||||
<controls1:EdgePointerImage.Image>
|
||||
<Image Source="/Assets/tr_grren.png" Width="15" Height="15"/>
|
||||
</controls:EdgePointerImage.Image>
|
||||
</controls:EdgePointerImage>-->
|
||||
</controls1:EdgePointerImage.Image>
|
||||
</controls1:EdgePointerImage>-->
|
||||
|
||||
<controls1:EdgeLabelControl x:Name="PART_edgeLabel"
|
||||
Content="{Binding Text}"
|
||||
|
|
|
@ -122,6 +122,9 @@
|
|||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPageDebug.xaml.cs">
|
||||
<DependentUpon>MainPageDebug.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -161,6 +164,15 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Common\debug_template.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="MainPageDebug.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<Page
|
||||
x:Class="METRO.SimpleGraph.MainPageDebug"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:METRO.SimpleGraph"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls1="using:GraphX.METRO.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<controls1:ZoomControl x:Name="zc" ViewFinderVisibility="Collapsed" Grid.Row="1">
|
||||
<local:GraphAreaExample x:Name="graph" />
|
||||
</controls1:ZoomControl>
|
||||
|
||||
<Grid Grid.Row="0" Margin="5">
|
||||
<Grid.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="20" />
|
||||
<Setter Property="Margin" Value="2" />
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="FontSize" Value="20" />
|
||||
<Setter Property="Margin" Value="2" />
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Padding" Value="1" />
|
||||
<Setter Property="Margin" Value="2" />
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Border Background="DarkSlateGray" Opacity=".8" Margin="-5" />
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Width="64" Height="64" Name="butGenerate">
|
||||
<Image Stretch="Uniform" Source="Assets/play.png" />
|
||||
</Button>
|
||||
<Button Width="64" Height="64" Name="butRelayout" >
|
||||
<Image Stretch="Uniform" Source="Assets/refresh.png" />
|
||||
</Button>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="Layout algorithm:" />
|
||||
<ComboBox x:Name="cboxLayout" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="Overlap algorithm:" />
|
||||
<ComboBox x:Name="cboxOverlap" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="Edge routing algorithm:" />
|
||||
<ComboBox x:Name="cboxEdgeRouting" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Image Source="Assets/MidLogo.png" Stretch="Uniform" Grid.Row="0" Grid.RowSpan="2"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom"
|
||||
Width="220" Opacity=".6"/>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,362 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using GraphX.METRO.Controls.Animations;
|
||||
using GraphX.METRO.Controls.Models;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
using GraphX.PCL.Logic.Algorithms.LayoutAlgorithms;
|
||||
using GraphX.PCL.Logic.Algorithms.OverlapRemoval;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace METRO.SimpleGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPageDebug : Page
|
||||
{
|
||||
public MainPageDebug()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
cboxLayout.ItemsSource = Enum.GetValues(typeof(LayoutAlgorithmTypeEnum)).Cast<LayoutAlgorithmTypeEnum>();//.Where(a=> a != LayoutAlgorithmTypeEnum.FR && a != LayoutAlgorithmTypeEnum.BoundedFR).ToArray();
|
||||
cboxOverlap.ItemsSource = Enum.GetValues(typeof(OverlapRemovalAlgorithmTypeEnum)).Cast<OverlapRemovalAlgorithmTypeEnum>();
|
||||
cboxEdgeRouting.ItemsSource = Enum.GetValues(typeof(EdgeRoutingAlgorithmTypeEnum)).Cast<EdgeRoutingAlgorithmTypeEnum>();
|
||||
|
||||
cboxLayout.SelectedItem = LayoutAlgorithmTypeEnum.LinLog;
|
||||
cboxOverlap.SelectedItem = OverlapRemovalAlgorithmTypeEnum.FSA;
|
||||
cboxEdgeRouting.SelectedItem = EdgeRoutingAlgorithmTypeEnum.None;
|
||||
|
||||
cboxLayout.SelectionChanged += cboxLayout_SelectionChanged;
|
||||
cboxOverlap.SelectionChanged += cboxOverlap_SelectionChanged;
|
||||
cboxEdgeRouting.SelectionChanged += cboxEdgeRouting_SelectionChanged;
|
||||
|
||||
butRelayout.Click += butRelayout_Click;
|
||||
butGenerate.Click += butGenerate_Click;
|
||||
graph.GenerateGraphFinished += OnFinishedLayout;
|
||||
graph.RelayoutFinished += OnFinishedLayout;
|
||||
graph.AlignAllEdgesLabels();
|
||||
Loaded += MainPage_Loaded;
|
||||
}
|
||||
|
||||
void OnFinishedLayout(object sender, EventArgs e)
|
||||
{
|
||||
zc.ZoomToFill();
|
||||
|
||||
var count = 0;
|
||||
|
||||
}
|
||||
|
||||
private async void butGenerate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
GraphAreaExample_Setup();
|
||||
|
||||
try
|
||||
{
|
||||
await graph.GenerateGraphAsync(true);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// User may have canceled
|
||||
}
|
||||
}
|
||||
|
||||
async void butRelayout_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var t0 = DateTime.Now;
|
||||
await graph.RelayoutGraphAsync();
|
||||
Debug.WriteLine("Time elapsed: {0}", DateTime.Now - t0);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// User may have canceled
|
||||
}
|
||||
}
|
||||
|
||||
void cboxEdgeRouting_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (graph.LogicCore == null) return;
|
||||
graph.LogicCore.DefaultEdgeRoutingAlgorithm = (EdgeRoutingAlgorithmTypeEnum)cboxEdgeRouting.SelectedItem;
|
||||
}
|
||||
|
||||
void cboxOverlap_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (graph.LogicCore == null) return;
|
||||
graph.LogicCore.DefaultOverlapRemovalAlgorithm = (OverlapRemovalAlgorithmTypeEnum)cboxOverlap.SelectedItem;
|
||||
}
|
||||
|
||||
void cboxLayout_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if(graph.LogicCore == null) return;
|
||||
var late = (LayoutAlgorithmTypeEnum) cboxLayout.SelectedItem;
|
||||
graph.LogicCore.DefaultLayoutAlgorithm = late;
|
||||
if (late == LayoutAlgorithmTypeEnum.BoundedFR)
|
||||
graph.LogicCore.DefaultLayoutAlgorithmParams
|
||||
= graph.LogicCore.AlgorithmFactory.CreateLayoutParameters(LayoutAlgorithmTypeEnum.BoundedFR);
|
||||
if (late == LayoutAlgorithmTypeEnum.FR)
|
||||
graph.LogicCore.DefaultLayoutAlgorithmParams
|
||||
= graph.LogicCore.AlgorithmFactory.CreateLayoutParameters(LayoutAlgorithmTypeEnum.FR);
|
||||
}
|
||||
|
||||
async void MainPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InitialSetup();
|
||||
GraphAreaExample_Setup();
|
||||
|
||||
try
|
||||
{
|
||||
//await graph.GenerateGraphAsync(true);
|
||||
graph.PreloadVertexes(graph.LogicCore.Graph);
|
||||
var count = 0;
|
||||
foreach (var item in graph.VertexList.Values.ToList())
|
||||
{
|
||||
if (count == 0)
|
||||
item.SetPosition(0, 0);
|
||||
if (count == 1)
|
||||
item.SetPosition(400, 0);
|
||||
|
||||
count++;
|
||||
}
|
||||
graph.GenerateAllEdges();
|
||||
graph.SetVerticesDrag(true);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// User may have canceled
|
||||
}
|
||||
|
||||
//graph.RelayoutGraph(true);
|
||||
//zc.ZoomToFill();
|
||||
//graph.VertexList.Values.ToList()[0].SetPosition(new Point(0, 0));
|
||||
//graph.VertexList.Values.ToList()[1].SetPosition(new Point(100, 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void AddEdge(GraphExample igraph, int index1, int index2, IReadOnlyList<DataVertex> vlist)
|
||||
{
|
||||
var dataEdge = new DataEdge(vlist[index1], vlist[index2])
|
||||
{
|
||||
Text = string.Format("Edge {0}{1}", vlist[index1].ID, vlist[index2].ID),
|
||||
VisualEdgeThickness = _rnd.Next(1, 4),
|
||||
VisualEdgeTransparency = 1.0,
|
||||
VisualColor = "#ffffff"
|
||||
};
|
||||
igraph.AddEdge(dataEdge);
|
||||
}
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
private GraphExample GraphExample_Setup()
|
||||
{
|
||||
var dataGraph = new GraphExample();
|
||||
|
||||
//debug
|
||||
|
||||
|
||||
dataGraph.AddVertex(new DataVertex("MyVertex " + 1) { ID = 1, VisualDiameter = 10, VisualInnerDiameter = 10 });
|
||||
dataGraph.AddVertex(new DataVertex("MyVertex " + 2) { ID = 2, VisualDiameter = 10, VisualInnerDiameter = 10 });
|
||||
var vlist = dataGraph.Vertices.ToList();
|
||||
AddEdge(dataGraph, 0, 1, vlist);
|
||||
return dataGraph;
|
||||
|
||||
|
||||
|
||||
switch ((LayoutAlgorithmTypeEnum)cboxLayout.SelectedItem)
|
||||
{
|
||||
case LayoutAlgorithmTypeEnum.EfficientSugiyama:
|
||||
case LayoutAlgorithmTypeEnum.Sugiyama:
|
||||
case LayoutAlgorithmTypeEnum.BoundedFR:
|
||||
case LayoutAlgorithmTypeEnum.FR:
|
||||
case LayoutAlgorithmTypeEnum.Tree:
|
||||
for (int i = 1; i < 14; i++)
|
||||
{
|
||||
var dataVertex = new DataVertex("MyVertex " + i) { ID = i, VisualDiameter = _rnd.Next(25, 50), VisualInnerDiameter = _rnd.Next(10, 22) };
|
||||
dataGraph.AddVertex(dataVertex);
|
||||
}
|
||||
vlist = dataGraph.Vertices.ToList();
|
||||
AddEdge(dataGraph, 0, 1, vlist);
|
||||
AddEdge(dataGraph, 0, 2, vlist);
|
||||
AddEdge(dataGraph, 1, 3, vlist);
|
||||
AddEdge(dataGraph, 1, 4, vlist);
|
||||
AddEdge(dataGraph, 2, 5, vlist);
|
||||
AddEdge(dataGraph, 2, 6, vlist);
|
||||
AddEdge(dataGraph, 2, 7, vlist);
|
||||
|
||||
AddEdge(dataGraph, 8, 9, vlist);
|
||||
AddEdge(dataGraph, 9, 10, vlist);
|
||||
AddEdge(dataGraph, 10, 7, vlist);
|
||||
AddEdge(dataGraph, 10, 11, vlist);
|
||||
AddEdge(dataGraph, 10, 12, vlist);
|
||||
|
||||
break;
|
||||
default:
|
||||
for (var i = 1; i < 11; i++)
|
||||
{
|
||||
var dataVertex = new DataVertex("MyVertex " + i) { ID = i, VisualDiameter = _rnd.Next(50, 100), VisualInnerDiameter = _rnd.Next(20, 45) };
|
||||
if (i == 2)
|
||||
dataVertex.LabelText += "\nMultiline!";
|
||||
dataGraph.AddVertex(dataVertex);
|
||||
}
|
||||
vlist = dataGraph.Vertices.ToList();
|
||||
AddEdge(dataGraph, 0, 1, vlist);
|
||||
|
||||
AddEdge(dataGraph, 1, 2, vlist);
|
||||
AddEdge(dataGraph, 1, 3, vlist);
|
||||
AddEdge(dataGraph, 1, 4, vlist);
|
||||
|
||||
AddEdge(dataGraph, 4, 5, vlist);
|
||||
AddEdge(dataGraph, 4, 6, vlist);
|
||||
|
||||
AddEdge(dataGraph, 2, 7, vlist);
|
||||
AddEdge(dataGraph, 2, 8, vlist);
|
||||
|
||||
AddEdge(dataGraph, 8, 9, vlist);
|
||||
|
||||
//add some cross references
|
||||
AddEdge(dataGraph, 4, 2, vlist);
|
||||
AddEdge(dataGraph, 4, 8, vlist);
|
||||
AddEdge(dataGraph, 9, 2, vlist);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* foreach (var item in graph.EdgesList)
|
||||
{
|
||||
//item.Value.LabelVerticalOffset = -40;
|
||||
item.Value.LabelAngle = 45;
|
||||
}*/
|
||||
|
||||
|
||||
return dataGraph;
|
||||
|
||||
/*ManipulationDelta += MainPage_ManipulationDelta;
|
||||
ManipulationMode = ManipulationModes.Scale;
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
var dataVertex = new DataVertex("MyVertex " + i) { ID = i };
|
||||
dataGraph.AddVertex(dataVertex);
|
||||
}
|
||||
|
||||
var vlist = dataGraph.Vertices.ToList();
|
||||
//var dataEdge = new DataEdge(vlist[0], vlist[1]) { Text = string.Format("{0} -> {1}", vlist[0], vlist[1]) };
|
||||
//dataGraph.AddEdge(dataEdge);
|
||||
var dataEdge = new DataEdge(vlist[2], vlist[3]) { Text = "23" };
|
||||
dataGraph.AddEdge(dataEdge);
|
||||
dataEdge = new DataEdge(vlist[3], vlist[2]) { Text = "32" };
|
||||
dataGraph.AddEdge(dataEdge);
|
||||
|
||||
return dataGraph;*/
|
||||
}
|
||||
|
||||
void MainPage_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void InitialSetup()
|
||||
{
|
||||
var logicCore = new GXLogicCoreExample();
|
||||
graph.LogicCore = logicCore;
|
||||
|
||||
var layParams = new LinLogLayoutParameters { IterationCount = 100 };
|
||||
logicCore.DefaultLayoutAlgorithm = LayoutAlgorithmTypeEnum.SimpleRandom;
|
||||
logicCore.DefaultLayoutAlgorithmParams = layParams;
|
||||
|
||||
logicCore.DefaultOverlapRemovalAlgorithmParams = logicCore.AlgorithmFactory.CreateOverlapRemovalParameters(OverlapRemovalAlgorithmTypeEnum.FSA);
|
||||
((OverlapRemovalParameters)logicCore.DefaultOverlapRemovalAlgorithmParams).HorizontalGap = 50;
|
||||
((OverlapRemovalParameters)logicCore.DefaultOverlapRemovalAlgorithmParams).VerticalGap = 50;
|
||||
|
||||
graph.MoveAnimation = AnimationFactory.CreateMoveAnimation(MoveAnimation.Move, TimeSpan.FromMilliseconds(500));
|
||||
graph.MoveAnimation.Completed += MoveAnimation_Completed;
|
||||
}
|
||||
|
||||
private void GraphAreaExample_Setup()
|
||||
{
|
||||
|
||||
var logicCore = graph.GetLogicCore<GXLogicCoreExample>();
|
||||
var dataGraph = GraphExample_Setup();
|
||||
logicCore.Graph = dataGraph;
|
||||
|
||||
|
||||
/*LogicCore.DefaultLayoutAlgorithmParams = LogicCore.AlgorithmFactory.CreateLayoutParameters(GraphX.LayoutAlgorithmTypeEnum.KK);
|
||||
((KKLayoutParameters)LogicCore.DefaultLayoutAlgorithmParams).MaxIterations = 100;
|
||||
cboxOverlap.SelectedItem = OverlapRemovalAlgorithmTypeEnum.FSA;
|
||||
|
||||
cboxEdgeRouting.SelectedItem = EdgeRoutingAlgorithmTypeEnum.SimpleER;
|
||||
LogicCore.AsyncAlgorithmCompute = false;
|
||||
|
||||
graph.SetVerticesHighlight(true, GraphControlType.VertexAndEdge, EdgesType.All);
|
||||
graph.SetEdgesHighlight(true, GraphControlType.VertexAndEdge);
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
switch ((LayoutAlgorithmTypeEnum) cboxLayout.SelectedItem)
|
||||
{
|
||||
case LayoutAlgorithmTypeEnum.EfficientSugiyama:
|
||||
logicCore.DefaultLayoutAlgorithmParams = new EfficientSugiyamaLayoutParameters { VertexDistance = 50 };
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch ((LayoutAlgorithmTypeEnum)cboxLayout.SelectedItem)
|
||||
{
|
||||
case LayoutAlgorithmTypeEnum.EfficientSugiyama:
|
||||
case LayoutAlgorithmTypeEnum.Sugiyama:
|
||||
case LayoutAlgorithmTypeEnum.BoundedFR:
|
||||
case LayoutAlgorithmTypeEnum.FR:
|
||||
case LayoutAlgorithmTypeEnum.Tree:
|
||||
cboxEdgeRouting.SelectedItem = EdgeRoutingAlgorithmTypeEnum.SimpleER;
|
||||
break;
|
||||
default:
|
||||
cboxEdgeRouting.SelectedItem = EdgeRoutingAlgorithmTypeEnum.SimpleER;
|
||||
break;
|
||||
}
|
||||
|
||||
logicCore.EnableParallelEdges = true;
|
||||
logicCore.ParallelEdgeDistance = 25;
|
||||
logicCore.EdgeCurvingEnabled = true;
|
||||
|
||||
graph.SetVerticesDrag(true, true);
|
||||
graph.SetVerticesMathShape(VertexShape.Circle);
|
||||
graph.ShowAllVerticesLabels();
|
||||
graph.ShowAllEdgesLabels();
|
||||
|
||||
//DEBUG
|
||||
graph.UseLayoutRounding = false;
|
||||
zc.UseLayoutRounding = false;
|
||||
|
||||
graph.ShowAllEdgesLabels(false);
|
||||
graph.LogicCore.ExternalEdgeRoutingAlgorithm = null;
|
||||
graph.LogicCore.DefaultEdgeRoutingAlgorithm = EdgeRoutingAlgorithmTypeEnum.None;
|
||||
graph.SetVerticesMathShape(VertexShape.Rectangle);
|
||||
//graph.MouseOverAnimation = AnimationFactory.CreateMouseOverAnimation(MouseOverAnimation.Scale);
|
||||
|
||||
/*cboxLayout_SelectionChanged(null, null);
|
||||
cboxOverlap_SelectionChanged(null, null);
|
||||
cboxEdgeRouting_SelectionChanged(null, null);*/
|
||||
}
|
||||
|
||||
void MoveAnimation_Completed(object sender, EventArgs e)
|
||||
{
|
||||
zc.ZoomToFill();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0.3" />
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GraphX;
|
||||
using System.Windows.Media;
|
||||
using GraphX;
|
||||
using GraphX.PCL.Common.Models;
|
||||
|
||||
namespace ShowcaseApp.WPF
|
||||
|
|
|
@ -2,7 +2,7 @@ using QuickGraph;
|
|||
|
||||
namespace ShowcaseApp.WPF
|
||||
{
|
||||
public class GraphExample : BidirectionalGraph<DataVertex, DataEdge>
|
||||
public class GraphExample : BidirectionalGraph<DataVertex, DataEdge>, IMutableBidirectionalGraph<DataVertex, DataEdge>
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using GraphX.Measure;
|
||||
using GraphX.PCL.Common.Interfaces;
|
||||
using GraphX.PCL.Logic.Algorithms.EdgeRouting;
|
||||
using QuickGraph;
|
||||
|
||||
namespace ShowcaseApp.WPF.ExampleModels
|
||||
{
|
||||
public class OrthEr<TVertex, TEdge, TGraph> : EdgeRoutingAlgorithmBase<TVertex, TEdge, TGraph>
|
||||
where TGraph : class, IMutableBidirectionalGraph<TVertex, TEdge>
|
||||
where TEdge : class, IGraphXEdge<TVertex>
|
||||
where TVertex : class, IGraphXVertex
|
||||
{
|
||||
|
||||
public OrthEr(TGraph graph, IDictionary<TVertex, Point> vertexPositions, IDictionary<TVertex, Rect> vertexSizes, IEdgeRoutingParameters parameters = null) :
|
||||
base(graph, vertexPositions, vertexSizes, parameters)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void Compute(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var edge in Graph.Edges)
|
||||
{
|
||||
var sourcePosition = VertexPositions[edge.Source];
|
||||
var targetPosition = VertexPositions[edge.Target];
|
||||
var sourceSize = VertexSizes[edge.Source];
|
||||
var targetSize = VertexSizes[edge.Target];
|
||||
|
||||
if (sourcePosition.X != targetPosition.X )
|
||||
{
|
||||
EdgeRoutes.Add(
|
||||
edge,
|
||||
new[]
|
||||
{
|
||||
new Point(0, 0),
|
||||
new Point(targetPosition.X + targetSize.Width / 2, sourcePosition.Y + sourceSize.Height / 2),
|
||||
new Point(0, 0)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
TextOptions.TextFormattingMode ="Ideal"
|
||||
UseLayoutRounding="False"
|
||||
ContentSource="/Pages/Introduction.xaml"
|
||||
Height="600" Width="999">
|
||||
Height="720" Width="1280">
|
||||
<mui:ModernWindow.TitleLinks>
|
||||
<mui:Link DisplayName="Settings" Source="/Pages/Settings.xaml" />
|
||||
<mui:Link DisplayName="Web" Source="http://panthernet.ru/forum/index.php?/forum/5-discussions/" />
|
||||
|
|
|
@ -31,11 +31,13 @@ namespace ShowcaseApp.WPF.Models
|
|||
}
|
||||
|
||||
#region GenerateDataGraph
|
||||
|
||||
/// <summary>
|
||||
/// Generate example graph data
|
||||
/// </summary>
|
||||
/// <param name="count">Items count</param>
|
||||
public static GraphExample GenerateDataGraph(int count)
|
||||
/// <param name="addEdges"></param>
|
||||
public static GraphExample GenerateDataGraph(int count, bool addEdges = true)
|
||||
{
|
||||
var graph = new GraphExample();
|
||||
|
||||
|
@ -44,6 +46,9 @@ namespace ShowcaseApp.WPF.Models
|
|||
|
||||
var vlist = graph.Vertices.ToList();
|
||||
var cnt = 1;
|
||||
|
||||
if (!addEdges) return graph;
|
||||
|
||||
foreach (var item in vlist)
|
||||
{
|
||||
if (Rand.Next(0, 50) > 25) continue;
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using GraphX;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
using GraphX.PCL.Logic.Algorithms.LayoutAlgorithms;
|
||||
using GraphX.PCL.Logic.Algorithms.OverlapRemoval;
|
||||
using GraphX.WPF.Controls.Animations;
|
||||
using GraphX.WPF.Controls.Models;
|
||||
using QuickGraph;
|
||||
using ShowcaseApp.WPF.ExampleModels;
|
||||
using ShowcaseApp.WPF.Models;
|
||||
using Point = GraphX.Measure.Point;
|
||||
|
||||
namespace ShowcaseApp.WPF.Pages
|
||||
{
|
||||
|
@ -49,20 +50,37 @@ namespace ShowcaseApp.WPF.Pages
|
|||
dg_Area.AlignAllEdgesLabels(true);
|
||||
dg_Area.GenerateGraph(true);*/
|
||||
|
||||
var logicCore = new LogicCoreExample() { Graph = ShowcaseHelper.GenerateDataGraph(25) };
|
||||
foreach (var item in logicCore.Graph.Vertices.Take(4))
|
||||
{
|
||||
item.SkipProcessing = ProcessingOptionEnum.Freeze;
|
||||
}
|
||||
var logicCore = new LogicCoreExample { Graph = ShowcaseHelper.GenerateDataGraph(5, false) };
|
||||
|
||||
var vlist = logicCore.Graph.Vertices.ToList();
|
||||
var edge = new DataEdge(vlist[0], vlist[1]);//{ SourceConnectionPointId = 2, TargetConnectionPointId = 1 };
|
||||
logicCore.Graph.AddEdge(edge);
|
||||
edge = new DataEdge(vlist[0], vlist[2]);//{ SourceConnectionPointId = 3, TargetConnectionPointId = 1 };
|
||||
logicCore.Graph.AddEdge(edge);
|
||||
edge = new DataEdge(vlist[2], vlist[3]);
|
||||
logicCore.Graph.AddEdge(edge);
|
||||
edge = new DataEdge(vlist[2], vlist[4]);
|
||||
logicCore.Graph.AddEdge(edge);
|
||||
|
||||
|
||||
//edge = new DataEdge(vlist[1], vlist[2]) { SourceConnectionPointId = 3, TargetConnectionPointId = 2 };
|
||||
//logicCore.Graph.AddEdge(edge);
|
||||
|
||||
logicCore.DefaultLayoutAlgorithm = LayoutAlgorithmTypeEnum.EfficientSugiyama;
|
||||
logicCore.DefaultLayoutAlgorithmParams = logicCore.AlgorithmFactory.CreateLayoutParameters(LayoutAlgorithmTypeEnum.EfficientSugiyama);
|
||||
((EfficientSugiyamaLayoutParameters)logicCore.DefaultLayoutAlgorithmParams).Direction = LayoutDirection.RightToLeft;
|
||||
((EfficientSugiyamaLayoutParameters)logicCore.DefaultLayoutAlgorithmParams).EdgeRouting = SugiyamaEdgeRoutings.Orthogonal;
|
||||
((EfficientSugiyamaLayoutParameters)logicCore.DefaultLayoutAlgorithmParams).LayerDistance = 100;
|
||||
((EfficientSugiyamaLayoutParameters) logicCore.DefaultLayoutAlgorithmParams).VertexDistance = 50;
|
||||
//logicCore.ExternalEdgeRoutingAlgorithm = new OrthEr<DataVertex, DataEdge, IMutableBidirectionalGraph<DataVertex, DataEdge>>(logicCore.Graph, null, null);
|
||||
|
||||
logicCore.DefaultLayoutAlgorithm = LayoutAlgorithmTypeEnum.Circular;
|
||||
logicCore.DefaultOverlapRemovalAlgorithm = OverlapRemovalAlgorithmTypeEnum.FSA;
|
||||
logicCore.DefaultOverlapRemovalAlgorithmParams = logicCore.AlgorithmFactory.CreateOverlapRemovalParameters(OverlapRemovalAlgorithmTypeEnum.FSA);
|
||||
((OverlapRemovalParameters)logicCore.DefaultOverlapRemovalAlgorithmParams).HorizontalGap = 50;
|
||||
((OverlapRemovalParameters)logicCore.DefaultOverlapRemovalAlgorithmParams).VerticalGap = 50;
|
||||
logicCore.DefaultEdgeRoutingAlgorithm = EdgeRoutingAlgorithmTypeEnum.SimpleER;
|
||||
logicCore.DefaultEdgeRoutingAlgorithm = EdgeRoutingAlgorithmTypeEnum.None;
|
||||
logicCore.AsyncAlgorithmCompute = false;
|
||||
logicCore.EdgeCurvingEnabled = true;
|
||||
logicCore.EdgeCurvingEnabled = false;
|
||||
|
||||
dg_Area.LogicCore = logicCore;
|
||||
dg_Area.MoveAnimation = AnimationFactory.CreateMoveAnimation(MoveAnimation.Move, TimeSpan.FromSeconds(0.5));
|
||||
|
@ -75,7 +93,7 @@ namespace ShowcaseApp.WPF.Pages
|
|||
dg_Area.GenerateGraph(true);
|
||||
foreach (var item in logicCore.Graph.Vertices.Take(4))
|
||||
{
|
||||
dg_Area.VertexList[item].SetPosition(new System.Windows.Point());
|
||||
// dg_Area.VertexList[item].SetPosition(new Point());
|
||||
}
|
||||
|
||||
//dg_Area.RelayoutGraph();
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GridSplitter VerticalAlignment="Stretch" Width="2" Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"/>
|
||||
|
||||
<Border CornerRadius="2" BorderBrush="{DynamicResource ButtonBorder}" Background="{DynamicResource ButtonBackground}" Margin="2" BorderThickness="1">
|
||||
<TextBlock Margin="1" TextWrapping="Wrap" TextAlignment="Center" Text="Here is an example of the edges routing and handling. You can preview different edge routing and compiling algorithms along with applied visual styles and drag vertices to see routed path changes."/>
|
||||
</Border>
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
d:DesignHeight="300" d:DesignWidth="600">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto" MinWidth="170"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height=" Auto"/>
|
||||
|
@ -40,7 +41,9 @@
|
|||
<Label VerticalAlignment="Center" Content="Async loading graph..." Margin="10"/>
|
||||
</Border>
|
||||
|
||||
<DockPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
|
||||
<GridSplitter VerticalAlignment="Stretch" Width="2" Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"/>
|
||||
|
||||
<DockPanel Grid.Column="2" Grid.Row="0" Grid.RowSpan="2">
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" HorizontalAlignment="Center">
|
||||
<Button Height="50" Margin="3" Name="gg_but_randomgraph" DockPanel.Dock="Top" UseLayoutRounding="True" BorderBrush="{DynamicResource ModernButtonBorder}">
|
||||
<Image Width="44" Source="/Assets/dice.png" Stretch="Uniform"/>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="180"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
|
@ -25,7 +26,9 @@
|
|||
</ResourceDictionary>
|
||||
</Grid.Resources>
|
||||
|
||||
<Border CornerRadius="2" BorderBrush="{DynamicResource ButtonBorder}" Background="{DynamicResource ButtonBackground}" Margin="2" BorderThickness="1">
|
||||
<GridSplitter VerticalAlignment="Stretch" Width="2" Grid.Row="0" Grid.RowSpan="3" Grid.Column="1"/>
|
||||
|
||||
<Border Grid.Row="0" Grid.Column="0" CornerRadius="2" BorderBrush="{DynamicResource ButtonBorder}" Background="{DynamicResource ButtonBackground}" Margin="2" BorderThickness="1">
|
||||
<TextBlock Margin="1" TextWrapping="Wrap" TextAlignment="Center" Text="This graph is designed for templating, animation and visual behaviours preview. See vertex and edge tooltips, templated highlights and animations."/>
|
||||
</Border>
|
||||
|
||||
|
@ -72,7 +75,7 @@
|
|||
<Label VerticalAlignment="Center" Content="Async loading graph..." Margin="10"/>
|
||||
</Border>
|
||||
|
||||
<DockPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
|
||||
<DockPanel Grid.Column="2" Grid.Row="0" Grid.RowSpan="2">
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" HorizontalAlignment="Center">
|
||||
<Button Height="50" Margin="3" Name="tg_but_randomgraph" Click="tg_but_randomgraph_Click" DockPanel.Dock="Top" UseLayoutRounding="True" BorderBrush="{DynamicResource ModernButtonBorder}">
|
||||
<Image Width="44" Height="41.1667" Source="/Assets/dice.png" Stretch="Uniform"/>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34014
|
||||
// Runtime Version:4.0.30319.34209
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ShowcaseApp.WPF.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace ShowcaseApp.WPF.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
|
@ -22,48 +22,40 @@ namespace ShowcaseApp.WPF.Properties
|
|||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ShowcaseApp.WPF.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34014
|
||||
// Runtime Version:4.0.30319.34209
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ShowcaseApp.WPF.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace ShowcaseApp.WPF.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,16 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ShowcaseApp.WPF</RootNamespace>
|
||||
<AssemblyName>ShowcaseApp.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0.3</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
|
@ -103,6 +104,7 @@
|
|||
<Compile Include="ExampleModels\GraphAreaExample.cs" />
|
||||
<Compile Include="ExampleModels\GraphExample.cs" />
|
||||
<Compile Include="ExampleModels\LogicCoreExample.cs" />
|
||||
<Compile Include="ExampleModels\OrthEr.cs" />
|
||||
<Compile Include="External\WindowGlow\GlowManager.cs" />
|
||||
<Compile Include="External\WindowGlow\GlowWindow.cs" />
|
||||
<Compile Include="External\WindowGlow\Win32.cs" />
|
||||
|
|
|
@ -55,9 +55,9 @@
|
|||
StrokeThickness="2" MinWidth="1" MinHeight="1"
|
||||
ToolTip="{TemplateBinding ToolTip}"
|
||||
x:Name="PART_edgePath"/>
|
||||
<!--<Path Stroke="{TemplateBinding Foreground}"
|
||||
<Path Stroke="{TemplateBinding Foreground}"
|
||||
StrokeThickness="2" MinWidth="1" MinHeight="1"
|
||||
x:Name="PART_edgeArrowPath"/>-->
|
||||
x:Name="PART_edgeArrowPath"/>
|
||||
<controls:EdgeLabelControl x:Name="PART_edgeLabel" Content="{Binding Edge.ToolTipText, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
|
||||
</Grid>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<controls:VisibilityToBoolConverter Inverted="True" Not="True" x:Key="BooleanToVisibility"/>
|
||||
<controls:VisibilityToBoolConverter Inverted="True" Not="True" x:Key="BooleanToVisibility"/>
|
||||
|
||||
<!-- EDGE CONTROL -->
|
||||
<Style TargetType="{x:Type controls:EdgeControl}">
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
|
||||
<Border x:Name="border"
|
||||
BorderBrush="White"
|
||||
Background="Beige"
|
||||
BorderThickness="1"
|
||||
CornerRadius="10,10,10,10"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter Content="{TemplateBinding Vertex}" />
|
||||
</Border>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter Content="{TemplateBinding Vertex}" Grid.Column="1"/>
|
||||
<controls:StaticVertexConnectionPoint Shape="Rectangle" Source="/Assets/dice.png" Width="10" Height="10" Id="1" Grid.Column="0" VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Vertical" Grid.Column="2">
|
||||
<controls:StaticVertexConnectionPoint Shape="Circle" Source="/Assets/circle_red.png" Width="10" Height="10" Id="2" VerticalAlignment="Center"/>
|
||||
<controls:StaticVertexConnectionPoint Shape="Circle" Source="/Assets/circle_blue.png" Width="10" Height="10" Id="3" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
@ -48,10 +53,12 @@
|
|||
StrokeThickness="2" MinWidth="1" MinHeight="1"
|
||||
ToolTip="{TemplateBinding ToolTip}"
|
||||
x:Name="PART_edgePath"/>
|
||||
<Path Stroke="{TemplateBinding Foreground}"
|
||||
<controls:EdgePointerPath x:Name="PART_EdgePointerForTarget" Data="M0,0.5 L1,1 1,0Z" Scale="8,8" Fill="Black" Opacity="239"/>
|
||||
|
||||
<!--<Path Stroke="{TemplateBinding Foreground}"
|
||||
StrokeThickness="2" MinWidth="1" MinHeight="1"
|
||||
x:Name="PART_edgeArrowPath"/>
|
||||
<controls:EdgeLabelControl x:Name="PART_edgeLabel" Content="{Binding Edge.Text, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}" />
|
||||
x:Name="PART_edgeArrowPath"/>-->
|
||||
<!--<controls:EdgeLabelControl x:Name="PART_edgeLabel" Content="{Binding Edge.Text, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}" />-->
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
|
Двоичные данные
ExternalDlls/FirstFloor.ModernUI.dll
Двоичные данные
ExternalDlls/FirstFloor.ModernUI.dll
Двоичный файл не отображается.
|
@ -10,8 +10,8 @@ using System.Windows.Shapes;
|
|||
using GraphX.PCL.Common.Enums;
|
||||
using GraphX.PCL.Common.Exceptions;
|
||||
using GraphX.PCL.Common.Interfaces;
|
||||
using GraphX.PCL.Common.Models;
|
||||
using GraphX.WPF.Controls.Models;
|
||||
using Rect = GraphX.Measure.Rect;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
|
@ -643,9 +643,12 @@ namespace GraphX.WPF.Controls
|
|||
|
||||
internal virtual void UpdateEdge(bool updateLabel = true)
|
||||
{
|
||||
if ((Visibility == Visibility.Visible || IsHiddenEdgesUpdated) && _linePathObject != null)
|
||||
if (Visibility == Visibility.Visible || IsHiddenEdgesUpdated)
|
||||
{
|
||||
if (_linePathObject == null)
|
||||
ApplyTemplate();
|
||||
PrepareEdgePath(true, null, updateLabel);
|
||||
if (_linePathObject == null) return;
|
||||
_linePathObject.Data = _linegeometry;
|
||||
_linePathObject.StrokeDashArray = StrokeDashArray;
|
||||
|
||||
|
@ -753,8 +756,8 @@ namespace GraphX.WPF.Controls
|
|||
Y = (useCurrentCoords ? GraphAreaBase.GetY(Target) : GraphAreaBase.GetFinalY(Target))
|
||||
};
|
||||
|
||||
var hasEpImgSource = _edgePointerForSource != null;
|
||||
var hasEpImgTarget = _edgePointerForTarget != null;
|
||||
var hasEpSource = _edgePointerForSource != null;
|
||||
var hasEpTarget = _edgePointerForTarget != null;
|
||||
|
||||
//if self looped edge
|
||||
if (IsSelfLooped)
|
||||
|
@ -770,9 +773,9 @@ namespace GraphX.WPF.Controls
|
|||
GeometryHelper.TryFreeze(_arrowgeometry);
|
||||
GeometryHelper.TryFreeze(_linegeometry);
|
||||
|
||||
if (hasEpImgSource)
|
||||
if (hasEpSource)
|
||||
_edgePointerForSource.Hide();
|
||||
if (hasEpImgTarget)
|
||||
if (hasEpTarget)
|
||||
_edgePointerForTarget.Hide();
|
||||
return;
|
||||
}
|
||||
|
@ -793,13 +796,40 @@ namespace GraphX.WPF.Controls
|
|||
//Point p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new Rect(sourceSize), (hasRouteInfo ? routeInformation[1] : (targetPos)), Source.VertexShape);
|
||||
//Point p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new Rect(targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2] : (sourcePos), Target.VertexShape);
|
||||
|
||||
var p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new Rect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), Source.VertexShape);
|
||||
var p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new Rect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), Target.VertexShape);
|
||||
var gEdge = Edge as IGraphXCommonEdge;
|
||||
Point p1;
|
||||
Point p2;
|
||||
|
||||
if (gEdge != null && gEdge.SourceConnectionPointId.HasValue)
|
||||
{
|
||||
var sourceCp = Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
|
||||
if (sourceCp.Shape == VertexShape.None) p1 = sourceCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var targetCpPos = gEdge.TargetConnectionPointId.HasValue ? Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true).RectangularSize.Center() : (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos));
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourceCp.RectangularSize.Center(), sourceCp.RectangularSize, targetCpPos, sourceCp.Shape);
|
||||
}
|
||||
}else
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new System.Windows.Rect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), Source.VertexShape);
|
||||
|
||||
if (gEdge != null && gEdge.TargetConnectionPointId.HasValue)
|
||||
{
|
||||
var targetCp = Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
|
||||
if (targetCp.Shape == VertexShape.None) p2 = targetCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetCp.RectangularSize.Center(), targetCp.RectangularSize, sourceCpPos, targetCp.Shape);
|
||||
}
|
||||
}
|
||||
else
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new System.Windows.Rect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), Target.VertexShape);
|
||||
|
||||
SourceConnectionPoint = p1;
|
||||
TargetConnectionPoint = p2;
|
||||
|
||||
_linegeometry = new PathGeometry(); PathFigure lineFigure;
|
||||
//TODO clear in 2.2.0 in favor of new arrow path logic
|
||||
_arrowgeometry = new PathGeometry(); PathFigure arrowFigure = null;
|
||||
|
||||
//if we have route and route consist of 2 or more points
|
||||
|
@ -815,6 +845,14 @@ namespace GraphX.WPF.Controls
|
|||
if (RootArea.EdgeCurvingEnabled)
|
||||
{
|
||||
var oPolyLineSegment = GeometryHelper.GetCurveThroughPoints(routePoints.ToArray(), 0.5, RootArea.EdgeCurvingTolerance);
|
||||
|
||||
if (hasEpTarget)
|
||||
{
|
||||
UpdateTargetEpData(oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 1], oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 2]);
|
||||
oPolyLineSegment.Points.RemoveAt(oPolyLineSegment.Points.Count - 1);
|
||||
}
|
||||
if (hasEpSource) UpdateSourceEpData(oPolyLineSegment.Points.First(), oPolyLineSegment.Points[1]);
|
||||
|
||||
lineFigure = GeometryHelper.GetPathFigureFromPathSegments(routePoints[0], true, true, oPolyLineSegment);
|
||||
//get two last points of curved path to generate correct arrow
|
||||
var cLast = oPolyLineSegment.Points.Last();
|
||||
|
@ -823,17 +861,16 @@ namespace GraphX.WPF.Controls
|
|||
arrowFigure = GeometryHelper.GenerateOldArrow(cPrev, cLast);
|
||||
//freeze and create resulting geometry
|
||||
GeometryHelper.TryFreeze(oPolyLineSegment);
|
||||
|
||||
if (hasEpImgTarget) UpdateTargetEpData(cLast, cPrev);
|
||||
if (hasEpImgSource) UpdateSourceEpData(oPolyLineSegment.Points.First(), oPolyLineSegment.Points[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasEpSource) UpdateSourceEpData(routePoints.First(), routePoints[1]);
|
||||
if (hasEpTarget)
|
||||
routePoints[routePoints.Count - 1] = (routePoints[routePoints.Count - 1] - (Vector)UpdateTargetEpData(p2, routePoints[routePoints.Count - 2]));
|
||||
|
||||
lineFigure = new PathFigure(p1, new PathSegment[] { new PolyLineSegment(routePoints.ToArray(), true) }, false);
|
||||
if(_arrowPathObject != null)
|
||||
arrowFigure = GeometryHelper.GenerateOldArrow(routePoints[routePoints.Count - 2], p2);
|
||||
if (hasEpImgSource) UpdateSourceEpData(routePoints.First(), routePoints[1]);
|
||||
if (hasEpImgTarget) UpdateTargetEpData(routePoints[routePoints.Count - 2], p2);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -843,23 +880,23 @@ namespace GraphX.WPF.Controls
|
|||
//Vector v = p1 - p2; v = v / v.Length * 5;
|
||||
// Vector n = new Vector(-v.Y, v.X) * 0.7;
|
||||
//segments[0] = new LineSegment(p2 + v, true);
|
||||
if (hasEpSource) UpdateSourceEpData(p1, p2);
|
||||
if (hasEpTarget)
|
||||
p2 = (Point)(p2 - UpdateTargetEpData(p2, p1));
|
||||
|
||||
lineFigure = new PathFigure(p1, new PathSegment[] { new LineSegment(p2, true) }, false);
|
||||
if(_arrowPathObject != null)
|
||||
arrowFigure = GeometryHelper.GenerateOldArrow(p1, p2);
|
||||
if (hasEpImgSource) UpdateSourceEpData(p1,p2);
|
||||
if (hasEpImgTarget) UpdateTargetEpData(p2,p1);
|
||||
|
||||
}
|
||||
GeometryHelper.TryFreeze(lineFigure);
|
||||
(_linegeometry as PathGeometry).Figures.Add(lineFigure);
|
||||
((PathGeometry) _linegeometry).Figures.Add(lineFigure);
|
||||
if (arrowFigure != null)
|
||||
{
|
||||
GeometryHelper.TryFreeze(arrowFigure);
|
||||
_arrowgeometry.Figures.Add(arrowFigure);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GeometryHelper.TryFreeze(_linegeometry);
|
||||
GeometryHelper.TryFreeze(_arrowgeometry);
|
||||
|
||||
|
@ -880,10 +917,10 @@ namespace GraphX.WPF.Controls
|
|||
_edgePointerForSource.Update(from, dir, _edgePointerForSource.NeedRotation ? -MathHelper.GetAngleBetweenPoints(from, to).ToDegrees() : 0);
|
||||
}
|
||||
|
||||
private void UpdateTargetEpData(Point from, Point to)
|
||||
private Point UpdateTargetEpData(Point from, Point to)
|
||||
{
|
||||
var dir = MathHelper.GetDirection(from, to);
|
||||
_edgePointerForTarget.Update(from, dir, _edgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
|
||||
return _edgePointerForTarget.Update(from, dir, _edgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -893,12 +930,12 @@ namespace GraphX.WPF.Controls
|
|||
Clean();
|
||||
}
|
||||
|
||||
public Measure.Rect GetLabelSize()
|
||||
public Rect GetLabelSize()
|
||||
{
|
||||
return _edgeLabelControl.GetSize().ToGraphX();
|
||||
}
|
||||
|
||||
public void SetCustomLabelSize(Rect rect)
|
||||
public void SetCustomLabelSize(System.Windows.Rect rect)
|
||||
{
|
||||
_edgeLabelControl.SetSize(rect);
|
||||
}
|
||||
|
@ -906,7 +943,7 @@ namespace GraphX.WPF.Controls
|
|||
internal void UpdateLabelLayout()
|
||||
{
|
||||
_edgeLabelControl.Show();
|
||||
if (_edgeLabelControl.GetSize() == Rect.Empty)// || double.IsNaN(_edgeLabelControl.Width))
|
||||
if (_edgeLabelControl.GetSize() == System.Windows.Rect.Empty)// || double.IsNaN(_edgeLabelControl.Width))
|
||||
{
|
||||
_edgeLabelControl.UpdateLayout();
|
||||
_edgeLabelControl.UpdatePosition();
|
||||
|
|
|
@ -86,10 +86,11 @@ namespace GraphX.WPF.Controls
|
|||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
public virtual void Update(Point? position, Vector direction, double angle = 0d)
|
||||
public virtual Point Update(Point? position, Vector direction, double angle = 0d)
|
||||
{
|
||||
var vecOffset = new Vector(direction.X * Offset.X, direction.Y * Offset.Y);
|
||||
position = position - new Vector(direction.X * Width * .5, direction.Y * Height * .5);// + vecOffset;
|
||||
//var vecOffset = new Vector(direction.X * Offset.X, direction.Y * Offset.Y);
|
||||
if (DesiredSize.Width == 0 || DesiredSize.Height == 0) return new Point();
|
||||
position = position - new Vector(direction.X * DesiredSize.Width * .5, direction.Y * DesiredSize.Height * .5);// + vecOffset;
|
||||
|
||||
if (position.HasValue && DesiredSize != Size.Empty)
|
||||
{
|
||||
|
@ -97,8 +98,9 @@ namespace GraphX.WPF.Controls
|
|||
Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
if(!NeedRotation) return;
|
||||
RenderTransform = new RotateTransform(angle, 0, 0);
|
||||
if(NeedRotation)
|
||||
RenderTransform = new RotateTransform(angle, 0, 0);
|
||||
return new Point(direction.X * DesiredSize.Width, direction.Y * DesiredSize.Height); ;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
public class EdgePointerPath: Shape, IEdgePointer
|
||||
{
|
||||
|
||||
#region PATH part
|
||||
|
||||
public static readonly DependencyProperty DataProperty =
|
||||
DependencyProperty.Register("Data", typeof(Geometry), typeof(EdgePointerPath), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender), null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets geometry that specifies the path to be drawn
|
||||
/// </summary>
|
||||
public Geometry Data { get { return (Geometry)GetValue(DataProperty); } set { SetValue(DataProperty, value); } }
|
||||
|
||||
protected override Geometry DefiningGeometry { get { return Data ?? Geometry.Empty; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common
|
||||
|
||||
internal Rect LastKnownRectSize;
|
||||
|
||||
public static readonly DependencyProperty NeedRotationProperty = DependencyProperty.Register("NeedRotation",
|
||||
typeof(bool),
|
||||
typeof(EdgePointerPath),
|
||||
new UIPropertyMetadata(true));
|
||||
/// <summary>
|
||||
/// Gets or sets if image has to be rotated according to edge directions
|
||||
/// </summary>
|
||||
public bool NeedRotation
|
||||
{
|
||||
get { return (bool)GetValue(NeedRotationProperty); }
|
||||
set { SetValue(NeedRotationProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private EdgeControl _edgeControl;
|
||||
protected EdgeControl EdgeControl { get { return _edgeControl ?? (_edgeControl = GetEdgeControl(VisualParent)); } }
|
||||
|
||||
public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale",
|
||||
typeof(Point),
|
||||
typeof(EdgePointerPath),
|
||||
new UIPropertyMetadata(new Point(1,1), ScalePropertyChangedCallback));
|
||||
|
||||
private static void ScalePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (EdgePointerPath) dependencyObject;
|
||||
obj.ScalePath((Point)e.NewValue);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path scale multiplier
|
||||
/// </summary>
|
||||
public Point Scale
|
||||
{
|
||||
get { return (Point)GetValue(ScaleProperty); }
|
||||
set { SetValue(ScaleProperty, value); }
|
||||
}
|
||||
|
||||
public EdgePointerPath()
|
||||
{
|
||||
RenderTransformOrigin = new Point(.5, .5);
|
||||
LayoutUpdated += EdgePointerImage_LayoutUpdated;
|
||||
}
|
||||
|
||||
|
||||
void EdgePointerImage_LayoutUpdated(object sender, EventArgs e)
|
||||
{
|
||||
if (LastKnownRectSize == Rect.Empty || double.IsNaN(LastKnownRectSize.Width) || LastKnownRectSize.Width == 0)
|
||||
{
|
||||
UpdateLayout();
|
||||
if (EdgeControl != null && !EdgeControl.IsSelfLooped)
|
||||
{
|
||||
EdgeControl.UpdateEdge(false);
|
||||
}
|
||||
}
|
||||
else Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="scale">Point scale value</param>
|
||||
public void ScalePath(Point scale)
|
||||
{
|
||||
var pathGeometry = Data.Clone();
|
||||
pathGeometry.Transform = new ScaleTransform(scale.X, scale.Y);
|
||||
Data = pathGeometry.GetFlattenedPathGeometry();
|
||||
}
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="x">X scale value</param>
|
||||
/// <param name="y">Y scale value</param>
|
||||
public void ScalePath(double x, double y)
|
||||
{
|
||||
ScalePath(new Point(x,y));
|
||||
}
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="value">Scale value</param>
|
||||
public void ScalePath(double value)
|
||||
{
|
||||
ScalePath(new Point(value, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
public virtual Point Update(Point? position, Vector direction, double angle = 0d)
|
||||
{
|
||||
if (DesiredSize.Width == 0 || DesiredSize.Height == 0) return new Point();
|
||||
position = position - new Vector(direction.X * DesiredSize.Width * .5, direction.Y * DesiredSize.Height * .5);
|
||||
|
||||
if (position.HasValue && DesiredSize != Size.Empty)
|
||||
{
|
||||
LastKnownRectSize = new Rect(new Point(position.Value.X - DesiredSize.Width * .5, position.Value.Y - DesiredSize.Height * .5), DesiredSize);
|
||||
Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
if(NeedRotation)
|
||||
RenderTransform = new RotateTransform(angle, 0, 0);
|
||||
return new Point(direction.X * DesiredSize.Width, direction.Y * DesiredSize.Height);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_edgeControl = null;
|
||||
}
|
||||
|
||||
#region Common methods
|
||||
public void Show()
|
||||
{
|
||||
Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private static EdgeControl GetEdgeControl(DependencyObject parent)
|
||||
{
|
||||
while (parent != null)
|
||||
{
|
||||
var control = parent as EdgeControl;
|
||||
if (control != null) return control;
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -665,7 +665,8 @@ namespace GraphX.WPF.Controls
|
|||
});
|
||||
|
||||
//Edge Routing
|
||||
if (eralg != null)
|
||||
var algEr = alg as ILayoutEdgeRouting<TEdge>;
|
||||
if (eralg != null && (algEr == null || algEr.EdgeRoutes == null || !algEr.EdgeRoutes.Any()))
|
||||
{
|
||||
RunOnDispatcherThread(() =>
|
||||
{
|
||||
|
@ -688,6 +689,12 @@ namespace GraphX.WPF.Controls
|
|||
LogicCore.CreateNewAlgorithmStorage(alg, overlap, eralg);
|
||||
});
|
||||
}
|
||||
|
||||
if (algEr != null && algEr.EdgeRoutes != null)
|
||||
{
|
||||
foreach (var item in algEr.EdgeRoutes)
|
||||
item.Key.RoutingPoints = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
using System.Windows;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
|
||||
{
|
||||
public interface IEdgePointer: IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -14,7 +13,7 @@ namespace GraphX.WPF.Controls
|
|||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
void Update(Point? position, Vector direction, double angle = 0d);
|
||||
Point Update(Point? position, Vector direction, double angle = 0d);
|
||||
|
||||
void Hide();
|
||||
void Show();
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
public interface IVertexConnectionPoint : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Connector identifier
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets shape form for connection point (affects math calculations for edge end placement)
|
||||
/// </summary>
|
||||
VertexShape Shape { get; }
|
||||
|
||||
void Hide();
|
||||
void Show();
|
||||
|
||||
Rect RectangularSize { get; }
|
||||
|
||||
void Update();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
public class StaticVertexConnectionPoint: Image, IVertexConnectionPoint
|
||||
{
|
||||
#region Common part
|
||||
|
||||
/// <summary>
|
||||
/// Connector identifier
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
|
||||
public static readonly DependencyProperty ShapeProperty =
|
||||
DependencyProperty.Register("Shape", typeof(VertexShape), typeof(VertexControl), new UIPropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets shape form for connection point (affects math calculations for edge end placement)
|
||||
/// </summary>
|
||||
public VertexShape Shape
|
||||
{
|
||||
get { return (VertexShape)GetValue(ShapeProperty); }
|
||||
set { SetValue(ShapeProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
private Rect _rectangularSize;
|
||||
public Rect RectangularSize {
|
||||
get
|
||||
{
|
||||
if(_rectangularSize == Rect.Empty)
|
||||
UpdateLayout();
|
||||
return _rectangularSize;
|
||||
}
|
||||
private set { _rectangularSize = value; }
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private static VertexControl GetVertexControl(DependencyObject parent)
|
||||
{
|
||||
while (parent != null)
|
||||
{
|
||||
var control = parent as VertexControl;
|
||||
if (control != null) return control;
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private VertexControl _vertexControl;
|
||||
protected VertexControl VertexControl { get { return _vertexControl ?? (_vertexControl = GetVertexControl(VisualParent)); } }
|
||||
|
||||
public StaticVertexConnectionPoint()
|
||||
{
|
||||
RenderTransformOrigin = new Point(.5, .5);
|
||||
LayoutUpdated += StaticVertexConnector_LayoutUpdated;
|
||||
}
|
||||
|
||||
void StaticVertexConnector_LayoutUpdated(object sender, EventArgs e)
|
||||
{
|
||||
var position = this.TranslatePoint(new Point(), VertexControl);
|
||||
var vPos = VertexControl.GetPosition();
|
||||
position = new Point(position.X + vPos.X, position.Y + vPos.Y);
|
||||
RectangularSize = new Rect(position, DesiredSize);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
UpdateLayout();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_vertexControl = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
using GraphX.PCL.Common.Exceptions;
|
||||
using GraphX.WPF.Controls.Models;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
|
@ -17,6 +19,12 @@ namespace GraphX.WPF.Controls
|
|||
public class VertexControl: Control, IGraphControl
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// List of found vertex connection points
|
||||
/// </summary>
|
||||
internal List<IVertexConnectionPoint> VertexConnectionPointsList = new List<IVertexConnectionPoint>();
|
||||
|
||||
/// <summary>
|
||||
/// Provides settings for event calls within single vertex control
|
||||
/// </summary>
|
||||
|
@ -215,6 +223,11 @@ namespace GraphX.WPF.Controls
|
|||
UpdateLayout();
|
||||
_vertexLabelControl.UpdatePosition();
|
||||
}
|
||||
|
||||
VertexConnectionPointsList = this.FindDescendantsOfType<IVertexConnectionPoint>().ToList();
|
||||
if(VertexConnectionPointsList.GroupBy(x => x.Id).Count(group => @group.Count() > 1) > 0)
|
||||
throw new GX_InvalidDataException("Vertex connection points in VertexControl template must have unique Id!");
|
||||
|
||||
}
|
||||
|
||||
#region Events handling
|
||||
|
@ -316,5 +329,17 @@ namespace GraphX.WPF.Controls
|
|||
var pos = GetPosition();
|
||||
return new Point(pos.X + ActualWidth * .5, pos.Y + ActualHeight * .5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns first connection point found with specified Id
|
||||
/// </summary>
|
||||
/// <param name="id">Connection point identifier</param>
|
||||
/// <param name="runUpdate">Update connection point size data if found</param>
|
||||
public IVertexConnectionPoint GetConnectionPointById(int id, bool runUpdate = false)
|
||||
{
|
||||
var result = VertexConnectionPointsList.FirstOrDefault(a => a.Id == id);
|
||||
if(result != null) result.Update();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,121 +15,147 @@
|
|||
***********************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GraphX.WPF.Controls
|
||||
{
|
||||
public static class VisualTreeHelperEx
|
||||
{
|
||||
public static DependencyObject FindAncestorByType( DependencyObject element, Type type, bool specificTypeOnly )
|
||||
public static class VisualTreeHelperEx
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
public static DependencyObject FindAncestorByType(DependencyObject element, Type type, bool specificTypeOnly)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if( specificTypeOnly ? ( element.GetType() == type )
|
||||
: ( element.GetType() == type ) || ( element.GetType().IsSubclassOf( type ) ) )
|
||||
return element;
|
||||
if (specificTypeOnly ? (element.GetType() == type)
|
||||
: (element.GetType() == type) || (element.GetType().IsSubclassOf(type)))
|
||||
return element;
|
||||
|
||||
return VisualTreeHelperEx.FindAncestorByType( VisualTreeHelper.GetParent( element ), type, specificTypeOnly );
|
||||
return VisualTreeHelperEx.FindAncestorByType(VisualTreeHelper.GetParent(element), type, specificTypeOnly);
|
||||
}
|
||||
|
||||
public static T FindAncestorByType<T>(DependencyObject depObj) where T : DependencyObject
|
||||
{
|
||||
if (depObj == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
if (depObj is T)
|
||||
{
|
||||
return (T) depObj;
|
||||
}
|
||||
|
||||
T parent = default(T);
|
||||
|
||||
parent = VisualTreeHelperEx.FindAncestorByType<T>(VisualTreeHelper.GetParent(depObj));
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByName(Visual element, string name)
|
||||
{
|
||||
if (element != null && (element is FrameworkElement) && (element as FrameworkElement).Name == name)
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).ApplyTemplate();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantByName(visual, name);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByType(Visual element, Type type)
|
||||
{
|
||||
return VisualTreeHelperEx.FindDescendantByType(element, type, true);
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByType(Visual element, Type type, bool specificTypeOnly)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if (specificTypeOnly ? (element.GetType() == type)
|
||||
: (element.GetType() == type) || (element.GetType().IsSubclassOf(type)))
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).ApplyTemplate();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantByType(visual, type, specificTypeOnly);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
#region Find descendants of type
|
||||
|
||||
public static IEnumerable<T> FindDescendantsOfType<T>(this Visual element) where T: class
|
||||
{
|
||||
if (element == null) yield break;
|
||||
if (element is T)
|
||||
yield return element as T;
|
||||
|
||||
var frameworkElement = element as FrameworkElement;
|
||||
if (frameworkElement != null)
|
||||
frameworkElement.ApplyTemplate();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
|
||||
if(visual == null) continue;
|
||||
foreach (var item in visual.FindDescendantsOfType<T>())
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static T FindDescendantByType<T>(Visual element) where T : Visual
|
||||
{
|
||||
Visual temp = VisualTreeHelperEx.FindDescendantByType(element, typeof (T));
|
||||
|
||||
return (T) temp;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantWithPropertyValue(Visual element,
|
||||
DependencyProperty dp, object value)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if (element.GetValue(dp).Equals(value))
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).ApplyTemplate();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantWithPropertyValue(visual, dp, value);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
}
|
||||
|
||||
public static T FindAncestorByType<T>( DependencyObject depObj ) where T : DependencyObject
|
||||
{
|
||||
if( depObj == null )
|
||||
{
|
||||
return default( T );
|
||||
}
|
||||
if( depObj is T )
|
||||
{
|
||||
return ( T )depObj;
|
||||
}
|
||||
|
||||
T parent = default( T );
|
||||
|
||||
parent = VisualTreeHelperEx.FindAncestorByType<T>( VisualTreeHelper.GetParent( depObj ) );
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByName( Visual element, string name )
|
||||
{
|
||||
if( element != null && ( element is FrameworkElement ) && ( element as FrameworkElement ).Name == name )
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).ApplyTemplate();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild( element, i ) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantByName( visual, name );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByType( Visual element, Type type )
|
||||
{
|
||||
return VisualTreeHelperEx.FindDescendantByType( element, type, true );
|
||||
}
|
||||
|
||||
public static Visual FindDescendantByType( Visual element, Type type, bool specificTypeOnly )
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
|
||||
if( specificTypeOnly ? ( element.GetType() == type )
|
||||
: ( element.GetType() == type ) || ( element.GetType().IsSubclassOf( type ) ) )
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).ApplyTemplate();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild( element, i ) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantByType( visual, type, specificTypeOnly );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static T FindDescendantByType<T>( Visual element ) where T : Visual
|
||||
{
|
||||
Visual temp = VisualTreeHelperEx.FindDescendantByType( element, typeof( T ) );
|
||||
|
||||
return ( T )temp;
|
||||
}
|
||||
|
||||
public static Visual FindDescendantWithPropertyValue( Visual element,
|
||||
DependencyProperty dp, object value )
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
|
||||
if( element.GetValue( dp ).Equals( value ) )
|
||||
return element;
|
||||
|
||||
Visual foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).ApplyTemplate();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
Visual visual = VisualTreeHelper.GetChild( element, i ) as Visual;
|
||||
foundElement = VisualTreeHelperEx.FindDescendantWithPropertyValue( visual, dp, value );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GraphX.WPF.Controls</RootNamespace>
|
||||
<AssemblyName>GraphX.WPF.Controls</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0.3</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
|
@ -24,6 +25,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
|
@ -92,6 +94,9 @@
|
|||
<Compile Include="Animations\MoveSimpleAnimation.cs" />
|
||||
<Compile Include="Behaviours\DragBehaviour.cs" />
|
||||
<Compile Include="Behaviours\HighlightBehaviour.cs" />
|
||||
<Compile Include="Controls\EdgePointers\EdgePointerPath.cs" />
|
||||
<Compile Include="Controls\Misc\IVertexConnectionPoint.cs" />
|
||||
<Compile Include="Controls\VertexConnectionPoints\StaticVertexConnectionPoint.cs" />
|
||||
<Compile Include="Controls\EdgePointers\EdgePointerImage.cs" />
|
||||
<Compile Include="ExceptionExtensions.cs" />
|
||||
<Compile Include="Controls\Misc\IEdgeLabelControl.cs" />
|
||||
|
|
|
@ -562,4 +562,5 @@
|
|||
</Setter>
|
||||
</Style>
|
||||
<!-- ENDREGION -->
|
||||
|
||||
</ResourceDictionary>
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
@ -53,5 +54,29 @@ namespace GraphX.WPF.Controls
|
|||
{
|
||||
return new Rect(rect.Left, rect.Top, rect.Width, rect.Height);
|
||||
}
|
||||
|
||||
public static Point Center(this Rect rect)
|
||||
{
|
||||
return new Point(rect.X + rect.Width * .5, rect.Y + rect.Height * .5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not for METRO
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> FindLogicalChildren<T>(this DependencyObject obj)
|
||||
where T : DependencyObject
|
||||
{
|
||||
if (obj == null) yield break;
|
||||
var child = obj as T;
|
||||
if (child != null) yield return child;
|
||||
|
||||
foreach (var c in LogicalTreeHelper.GetChildren(obj)
|
||||
.OfType<DependencyObject>()
|
||||
.SelectMany(FindLogicalChildren<T>))
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -582,9 +582,12 @@ namespace GraphX.METRO.Controls
|
|||
|
||||
internal void UpdateEdge(bool updateLabel = true)
|
||||
{
|
||||
if ((Visibility == Visibility.Visible || IsHiddenEdgesUpdated) && _linePathObject != null)
|
||||
if (Visibility == Visibility.Visible || IsHiddenEdgesUpdated)
|
||||
{
|
||||
if (_linePathObject == null)
|
||||
ApplyTemplate();
|
||||
PrepareEdgePath(true, null, updateLabel);
|
||||
if (_linePathObject == null) return;
|
||||
_linePathObject.Data = _linegeometry;
|
||||
_linePathObject.StrokeDashArray = StrokeDashArray;
|
||||
|
||||
|
@ -695,8 +698,8 @@ namespace GraphX.METRO.Controls
|
|||
Y = (useCurrentCoords ? GraphAreaBase.GetY(Target) : GraphAreaBase.GetFinalY(Target))
|
||||
};
|
||||
|
||||
var hasEpImgSource = _edgePointerForSource != null;
|
||||
var hasEpImgTarget = _edgePointerForTarget != null;
|
||||
var hasEpSource = _edgePointerForSource != null;
|
||||
var hasEpTarget = _edgePointerForTarget != null;
|
||||
|
||||
//if self looped edge
|
||||
if (IsSelfLooped)
|
||||
|
@ -711,9 +714,9 @@ namespace GraphX.METRO.Controls
|
|||
_arrowgeometry.Figures.Add(GeometryHelper.GenerateArrow(aPoint, new Point(), new Point(), dArrowAngle));
|
||||
_linegeometry = geo;
|
||||
|
||||
if (hasEpImgSource)
|
||||
if (hasEpSource)
|
||||
_edgePointerForSource.Hide();
|
||||
if (hasEpImgTarget)
|
||||
if (hasEpTarget)
|
||||
_edgePointerForTarget.Hide();
|
||||
return;
|
||||
}
|
||||
|
@ -730,17 +733,41 @@ namespace GraphX.METRO.Controls
|
|||
|
||||
/* Rectangular shapes implementation by bleibold */
|
||||
|
||||
var gEdge = Edge as IGraphXCommonEdge;
|
||||
Point p1;
|
||||
Point p2;
|
||||
|
||||
//Point p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new Rect(sourceSize), (hasRouteInfo ? routeInformation[1] : (targetPos)), Source.VertexShape);
|
||||
//Point p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new Rect(targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2] : (sourcePos), Target.VertexShape);
|
||||
if (gEdge != null && gEdge.SourceConnectionPointId.HasValue)
|
||||
{
|
||||
var sourceCp = Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
|
||||
if (sourceCp.Shape == VertexShape.None) p1 = sourceCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var targetCpPos = gEdge.TargetConnectionPointId.HasValue ? Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true).RectangularSize.Center() : (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos));
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourceCp.RectangularSize.Center(), sourceCp.RectangularSize, targetCpPos, sourceCp.Shape);
|
||||
}
|
||||
}
|
||||
else
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new Windows.Foundation.Rect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), Source.VertexShape);
|
||||
|
||||
var p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new Windows.Foundation.Rect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), Source.VertexShape);
|
||||
var p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new Windows.Foundation.Rect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), Target.VertexShape);
|
||||
if (gEdge != null && gEdge.TargetConnectionPointId.HasValue)
|
||||
{
|
||||
var targetCp = Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
|
||||
if (targetCp.Shape == VertexShape.None) p2 = targetCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetCp.RectangularSize.Center(), targetCp.RectangularSize, sourceCpPos, targetCp.Shape);
|
||||
}
|
||||
}
|
||||
else
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new Windows.Foundation.Rect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), Target.VertexShape);
|
||||
|
||||
SourceConnectionPoint = p1;
|
||||
TargetConnectionPoint = p2;
|
||||
|
||||
_linegeometry = new PathGeometry(); PathFigure lineFigure;
|
||||
//TODO clear in 2.2.0 in favor of new arrow path logic
|
||||
_arrowgeometry = new PathGeometry(); PathFigure arrowFigure;
|
||||
|
||||
//if we have route and route consist of 2 or more points
|
||||
|
@ -756,56 +783,49 @@ namespace GraphX.METRO.Controls
|
|||
if (RootArea.EdgeCurvingEnabled)
|
||||
{
|
||||
var oPolyLineSegment = GeometryHelper.GetCurveThroughPoints(routePoints.ToArray(), 0.5, RootArea.EdgeCurvingTolerance);
|
||||
|
||||
if (hasEpTarget)
|
||||
{
|
||||
UpdateTargetEpData(oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 1], oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 2]);
|
||||
oPolyLineSegment.Points.RemoveAt(oPolyLineSegment.Points.Count - 1);
|
||||
}
|
||||
if (hasEpSource) UpdateSourceEpData(oPolyLineSegment.Points.First(), oPolyLineSegment.Points[1]);
|
||||
|
||||
lineFigure = GeometryHelper.GetPathFigureFromPathSegments(routePoints[0], true, true, oPolyLineSegment);
|
||||
//get two last points of curved path to generate correct arrow
|
||||
var cLast = oPolyLineSegment.Points.Last();
|
||||
var cPrev = oPolyLineSegment.Points.Count == 1 ? oPolyLineSegment.Points.Last() : oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 2];
|
||||
arrowFigure = GeometryHelper.GenerateOldArrow(cPrev, cLast);
|
||||
//if(_endEdgePointerImage != null)
|
||||
// _endEdgePointerImage.Update(cLast);
|
||||
//freeze and create resulting geometry
|
||||
if (hasEpImgTarget) UpdateTargetEpData(cLast, cPrev);
|
||||
if (hasEpImgSource) UpdateSourceEpData(oPolyLineSegment.Points.First(), oPolyLineSegment.Points[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasEpSource) UpdateSourceEpData(routePoints.First(), routePoints[1]);
|
||||
if (hasEpTarget)
|
||||
routePoints[routePoints.Count - 1] = routePoints[routePoints.Count - 1].Subtract(UpdateTargetEpData(p2, routePoints[routePoints.Count - 2]));
|
||||
|
||||
var pcol = new PointCollection();
|
||||
foreach(var item in routePoints)
|
||||
pcol.Add(item);
|
||||
|
||||
lineFigure = new PathFigure {StartPoint = p1, Segments = new PathSegmentCollection {new PolyLineSegment {Points = pcol}}, IsClosed = false};
|
||||
arrowFigure = GeometryHelper.GenerateOldArrow(routePoints[routePoints.Count - 2], p2);
|
||||
if (hasEpImgSource) UpdateSourceEpData(routePoints.First(), routePoints[1]);
|
||||
if (hasEpImgTarget) UpdateTargetEpData(routePoints[routePoints.Count - 2], p2);
|
||||
}
|
||||
|
||||
}
|
||||
else // no route defined
|
||||
{
|
||||
//!!! Here is the line calculation to not overlap an arrowhead
|
||||
//Vector v = p1 - p2; v = v / v.Length * 5;
|
||||
// Vector n = new Vector(-v.Y, v.X) * 0.7;
|
||||
//segments[0] = new LineSegment(p2 + v, true);
|
||||
if (hasEpSource) UpdateSourceEpData(p1, p2);
|
||||
if (hasEpTarget)
|
||||
p2 = p2.Subtract(UpdateTargetEpData(p2, p1));
|
||||
|
||||
lineFigure = new PathFigure {StartPoint = p1, Segments = new PathSegmentCollection {new LineSegment() {Point = p2}}, IsClosed = false};
|
||||
arrowFigure = GeometryHelper.GenerateOldArrow(p1, p2);
|
||||
if (hasEpImgSource) UpdateSourceEpData(p1, p2);
|
||||
if (hasEpImgTarget) UpdateTargetEpData(p2, p1);
|
||||
//Debug.WriteLine("p1: " + p1 + " p2: " + p2);
|
||||
|
||||
}
|
||||
//GeometryHelper.TryFreeze(lineFigure);
|
||||
(_linegeometry as PathGeometry).Figures.Add(lineFigure);
|
||||
((PathGeometry) _linegeometry).Figures.Add(lineFigure);
|
||||
if (arrowFigure != null)
|
||||
{
|
||||
// GeometryHelper.TryFreeze(arrowFigure);
|
||||
_arrowgeometry.Figures.Add(arrowFigure);
|
||||
}
|
||||
//GeometryHelper.TryFreeze(_linegeometry);
|
||||
//GeometryHelper.TryFreeze(_arrowgeometry);
|
||||
|
||||
if (ShowLabel && _edgeLabelControl != null && _updateLabelPosition && updateLabel)
|
||||
_edgeLabelControl.UpdatePosition();
|
||||
//PathGeometry = (PathGeometry)_linegeometry;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -820,10 +840,10 @@ namespace GraphX.METRO.Controls
|
|||
_edgePointerForSource.Update(from, dir, _edgePointerForSource.NeedRotation ? -MathHelper.GetAngleBetweenPoints(from, to).ToDegrees() : 0);
|
||||
}
|
||||
|
||||
private void UpdateTargetEpData(Point from, Point to)
|
||||
private Point UpdateTargetEpData(Point from, Point to)
|
||||
{
|
||||
var dir = MathHelper.GetDirection(from, to);
|
||||
_edgePointerForTarget.Update(from, dir, _edgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
|
||||
return _edgePointerForTarget.Update(from, dir, _edgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -100,23 +100,17 @@ namespace GraphX.METRO.Controls
|
|||
|
||||
void EdgePointerImage_LayoutUpdated(object sender, object e)
|
||||
{
|
||||
/*if (LastKnownRectSize == Rect.Empty || double.IsNaN(LastKnownRectSize.Width) || LastKnownRectSize.Width == 0 || double.IsNaN(LastKnownRectSize.X))
|
||||
{
|
||||
UpdateLayout();
|
||||
if (EdgeControl != null && !EdgeControl.IsSelfLooped)
|
||||
{
|
||||
EdgeControl.UpdateEdge(false);
|
||||
}
|
||||
}
|
||||
else Arrange(LastKnownRectSize);*/
|
||||
if (LastKnownRectSize != Rect.Empty && !double.IsNaN(LastKnownRectSize.Width) && LastKnownRectSize.Width != 0)
|
||||
Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
public void Update(Point? position, Vector direction, double angle = 0d)
|
||||
public Point Update(Point? position, Vector direction, double angle = 0d)
|
||||
{
|
||||
var vecOffset = new Vector(direction.X * Offset.X, direction.Y * Offset.Y);
|
||||
//var vecOffset = new Vector(direction.X * Offset.X, direction.Y * Offset.Y);
|
||||
if (DesiredSize.Width == 0 || DesiredSize.Height == 0) return new Point();
|
||||
var vecMove = new Vector(direction.X * ActualWidth * .5, direction.Y * ActualHeight * .5);
|
||||
position = new Point(position.Value.X - vecMove.X, position.Value.Y - vecMove.Y);// + vecOffset;
|
||||
|
||||
|
@ -125,15 +119,11 @@ namespace GraphX.METRO.Controls
|
|||
LastKnownRectSize = new Rect(new Point(position.Value.X - DesiredSize.Width * .5, position.Value.Y - DesiredSize.Height * .5), DesiredSize);
|
||||
Measure(LastKnownRectSize.Size());
|
||||
Arrange(LastKnownRectSize);
|
||||
Debug.WriteLine("POS: "+position + " dir: "+ direction + " vecMoce: "+vecMove);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if(!NeedRotation) return;
|
||||
RenderTransform = new RotateTransform { Angle = angle, CenterX = 0, CenterY = 0 };
|
||||
if(NeedRotation)
|
||||
RenderTransform = new RotateTransform { Angle = angle, CenterX = 0, CenterY = 0 };
|
||||
return new Point(direction.X * ActualWidth, direction.Y * ActualHeight);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
using GraphX.Measure;
|
||||
using Point = Windows.Foundation.Point;
|
||||
using Rect = Windows.Foundation.Rect;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
{
|
||||
public class EdgePointerPath: ContentControl, IEdgePointer
|
||||
{
|
||||
|
||||
#region PATH part
|
||||
|
||||
public static readonly DependencyProperty PathProperty =
|
||||
DependencyProperty.Register("Path", typeof(Path), typeof(EdgePointerPath), new PropertyMetadata(null, PathChangedCallback));
|
||||
|
||||
private static void PathChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||
{
|
||||
var parent = dependencyObject as EdgePointerPath;
|
||||
if (parent == null)
|
||||
throw new Exception("EdgePointerPath -> ImageChangedCallback: Parent not found!");
|
||||
parent.Content = dependencyPropertyChangedEventArgs.NewValue;
|
||||
parent.ScalePath(parent.Scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets geometry that specifies the path to be drawn
|
||||
/// </summary>
|
||||
public Path Path { get { return (Path)GetValue(PathProperty); } set { SetValue(PathProperty, value); } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common
|
||||
|
||||
internal Rect LastKnownRectSize;
|
||||
|
||||
public static readonly DependencyProperty NeedRotationProperty = DependencyProperty.Register("NeedRotation",
|
||||
typeof(bool),
|
||||
typeof(EdgePointerPath),
|
||||
new PropertyMetadata(true));
|
||||
/// <summary>
|
||||
/// Gets or sets if image has to be rotated according to edge directions
|
||||
/// </summary>
|
||||
public bool NeedRotation
|
||||
{
|
||||
get { return (bool)GetValue(NeedRotationProperty); }
|
||||
set { SetValue(NeedRotationProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private EdgeControl _edgeControl;
|
||||
protected EdgeControl EdgeControl { get { return _edgeControl ?? (_edgeControl = GetEdgeControl(Parent)); } }
|
||||
|
||||
public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale",
|
||||
typeof(Point),
|
||||
typeof(EdgePointerPath),
|
||||
new PropertyMetadata(new Point(1,1), ScalePropertyChangedCallback));
|
||||
|
||||
private static void ScalePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var obj = (EdgePointerPath) dependencyObject;
|
||||
obj.ScalePath((Point)e.NewValue);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path scale multiplier
|
||||
/// </summary>
|
||||
public Point Scale
|
||||
{
|
||||
get { return (Point)GetValue(ScaleProperty); }
|
||||
set { SetValue(ScaleProperty, value); }
|
||||
}
|
||||
|
||||
public EdgePointerPath()
|
||||
{
|
||||
RenderTransformOrigin = new Point(.5, .5);
|
||||
LayoutUpdated += EdgePointerPath_LayoutUpdated;
|
||||
}
|
||||
|
||||
void EdgePointerPath_LayoutUpdated(object sender, object e)
|
||||
{
|
||||
if (LastKnownRectSize != Rect.Empty && !double.IsNaN(LastKnownRectSize.Width) && LastKnownRectSize.Width != 0)
|
||||
Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="scale">Point scale value</param>
|
||||
public void ScalePath(Point scale)
|
||||
{
|
||||
if(Path != null)
|
||||
Path.Data.Transform = new ScaleTransform() { ScaleX = scale.X, ScaleY = scale.Y };
|
||||
}
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="x">X scale value</param>
|
||||
/// <param name="y">Y scale value</param>
|
||||
public void ScalePath(double x, double y)
|
||||
{
|
||||
ScalePath(new Point(x,y));
|
||||
}
|
||||
/// <summary>
|
||||
/// Scales path by provided value
|
||||
/// </summary>
|
||||
/// <param name="value">Scale value</param>
|
||||
public void ScalePath(double value)
|
||||
{
|
||||
ScalePath(new Point(value, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
public virtual Point Update(Point? position, Vector direction, double angle = 0d)
|
||||
{
|
||||
if (DesiredSize.Width == 0 || DesiredSize.Height == 0) return new Point();
|
||||
var vec = new Vector(direction.X * ActualWidth * .5, direction.Y * ActualHeight * .5);
|
||||
position = position.HasValue ? (Point?)new Point(position.Value.X - vec.X, position.Value.Y - vec.Y) : null;
|
||||
|
||||
if (!double.IsNaN(position.Value.X))
|
||||
{
|
||||
LastKnownRectSize = new Rect(new Point(position.Value.X - DesiredSize.Width * .5, position.Value.Y - DesiredSize.Height * .5), DesiredSize);
|
||||
//Measure(LastKnownRectSize.Size());
|
||||
Arrange(LastKnownRectSize);
|
||||
}
|
||||
|
||||
if(NeedRotation)
|
||||
RenderTransform = new RotateTransform() { Angle = angle, CenterX = 0, CenterY = 0 };
|
||||
//LastKnownRectSize = new Rect(0,0, 1, 1);
|
||||
return new Point(direction.X * ActualWidth, direction.Y * ActualHeight);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_edgeControl = null;
|
||||
}
|
||||
|
||||
#region Common methods
|
||||
public void Show()
|
||||
{
|
||||
Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private static EdgeControl GetEdgeControl(DependencyObject parent)
|
||||
{
|
||||
while (parent != null)
|
||||
{
|
||||
var control = parent as EdgeControl;
|
||||
if (control != null) return control;
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -617,7 +617,6 @@ namespace GraphX.METRO.Controls
|
|||
resultCoords = new Dictionary<TVertex, Measure.Point>();
|
||||
foreach (var res in overlap.Rectangles)
|
||||
resultCoords.Add(res.Key, new Measure.Point(res.Value.Left, res.Value.Top));
|
||||
//if (Worker != null) Worker.ReportProgress(66, 1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1050,7 +1049,8 @@ namespace GraphX.METRO.Controls
|
|||
//setup path
|
||||
if (_svShowEdgeLabels == true)
|
||||
edgectrl.ShowLabel = true;
|
||||
edgectrl.PrepareEdgePath();
|
||||
//TODO check it
|
||||
else edgectrl.PrepareEdgePath();
|
||||
//edgectrl.InvalidateChildren();
|
||||
}
|
||||
//this.InvalidateVisual();
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace GraphX.METRO.Controls
|
|||
/// <summary>
|
||||
/// Update edge pointer position and angle
|
||||
/// </summary>
|
||||
void Update(Windows.Foundation.Point? position, Vector direction, double angle = 0d);
|
||||
Windows.Foundation.Point Update(Windows.Foundation.Point? position, Vector direction, double angle = 0d);
|
||||
|
||||
void Hide();
|
||||
void Show();
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using Windows.Foundation;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
{
|
||||
public interface IVertexConnectionPoint : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Connector identifier
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
void Hide();
|
||||
void Show();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets shape form for connection point (affects math calculations for edge end placement)
|
||||
/// </summary>
|
||||
VertexShape Shape { get; }
|
||||
|
||||
Rect RectangularSize { get; }
|
||||
|
||||
void Update();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
{
|
||||
public class StaticVertexConnectionPoint: ContentControl, IVertexConnectionPoint
|
||||
{
|
||||
#region Common part
|
||||
|
||||
/// <summary>
|
||||
/// Connector identifier
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
|
||||
public static readonly DependencyProperty ShapeProperty =
|
||||
DependencyProperty.Register("Shape", typeof(VertexShape), typeof(VertexControl), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets shape form for connection point (affects math calculations for edge end placement)
|
||||
/// </summary>
|
||||
public VertexShape Shape
|
||||
{
|
||||
get { return (VertexShape)GetValue(ShapeProperty); }
|
||||
set { SetValue(ShapeProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
private Rect _rectangularSize;
|
||||
public Rect RectangularSize {
|
||||
get
|
||||
{
|
||||
if(_rectangularSize == Rect.Empty)
|
||||
UpdateLayout();
|
||||
return _rectangularSize;
|
||||
}
|
||||
private set { _rectangularSize = value; }
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private static VertexControl GetVertexControl(DependencyObject parent)
|
||||
{
|
||||
while (parent != null)
|
||||
{
|
||||
var control = parent as VertexControl;
|
||||
if (control != null) return control;
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register("Image",
|
||||
typeof(Image),
|
||||
typeof(EdgePointerImage),
|
||||
new PropertyMetadata(null, ImageChangedCallback));
|
||||
|
||||
private static void ImageChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||
{
|
||||
var parent = dependencyObject as EdgePointerImage;
|
||||
if (parent == null)
|
||||
throw new Exception("EdgePointerImage -> ImageChangedCallback: Parent not found!");
|
||||
parent.Content = dependencyPropertyChangedEventArgs.NewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image for edge pointer
|
||||
/// </summary>
|
||||
public Image Image
|
||||
{
|
||||
get { return (Image)GetValue(ImageProperty); }
|
||||
set { SetValue(ImageProperty, value); }
|
||||
}
|
||||
|
||||
private VertexControl _vertexControl;
|
||||
protected VertexControl VertexControl { get { return _vertexControl ?? (_vertexControl = GetVertexControl(Parent)); } }
|
||||
|
||||
public StaticVertexConnectionPoint()
|
||||
{
|
||||
RenderTransformOrigin = new Point(.5, .5);
|
||||
LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
private void OnLayoutUpdated(object sender, object o)
|
||||
{
|
||||
var position = TransformToVisual(VertexControl).TransformPoint(new Point());
|
||||
var vPos = VertexControl.GetPosition();
|
||||
position = new Point(position.X + vPos.X, position.Y + vPos.Y);
|
||||
RectangularSize = new Rect(position, DesiredSize);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
UpdateLayout();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_vertexControl = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
|
@ -7,6 +8,7 @@ using Windows.UI.Xaml.Data;
|
|||
using Windows.UI.Xaml.Input;
|
||||
using GraphX.METRO.Controls.Models;
|
||||
using GraphX.PCL.Common.Enums;
|
||||
using GraphX.PCL.Common.Exceptions;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
{
|
||||
|
@ -22,6 +24,13 @@ namespace GraphX.METRO.Controls
|
|||
public class VertexControl : Control, IGraphControl
|
||||
{
|
||||
#region Properties
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// List of found vertex connection points
|
||||
/// </summary>
|
||||
internal List<IVertexConnectionPoint> VertexConnectionPointsList = new List<IVertexConnectionPoint>();
|
||||
|
||||
/// <summary>
|
||||
/// Provides settings for event calls within single vertex control
|
||||
/// </summary>
|
||||
|
@ -228,6 +237,10 @@ namespace GraphX.METRO.Controls
|
|||
UpdateLayout();
|
||||
_vertexLabelControl.UpdatePosition();
|
||||
}
|
||||
|
||||
VertexConnectionPointsList = this.FindDescendantsOfType<IVertexConnectionPoint>().ToList();
|
||||
if (VertexConnectionPointsList.GroupBy(x => x.Id).Count(group => @group.Count() > 1) > 0)
|
||||
throw new GX_InvalidDataException("Vertex connection points in VertexControl template must have unique Id!");
|
||||
}
|
||||
|
||||
#region Events handling
|
||||
|
@ -328,5 +341,16 @@ namespace GraphX.METRO.Controls
|
|||
return new Point(pos.X + ActualWidth * .5, pos.Y + ActualHeight * .5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns first connection point found with specified Id
|
||||
/// </summary>
|
||||
/// <param name="id">Connection point identifier</param>
|
||||
/// <param name="runUpdate">Update connection point size data if found</param>
|
||||
public IVertexConnectionPoint GetConnectionPointById(int id, bool runUpdate = false)
|
||||
{
|
||||
var result = VertexConnectionPointsList.FirstOrDefault(a => a.Id == id);
|
||||
if (result != null) result.Update();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,119 +15,143 @@
|
|||
***********************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
{
|
||||
public static class VisualTreeHelperEx
|
||||
{
|
||||
public static DependencyObject FindAncestorByType( DependencyObject element, Type type, bool specificTypeOnly )
|
||||
public static class VisualTreeHelperEx
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
public static DependencyObject FindAncestorByType(DependencyObject element, Type type, bool specificTypeOnly)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if( element.GetType() == type )
|
||||
return element;
|
||||
if (element.GetType() == type)
|
||||
return element;
|
||||
|
||||
return FindAncestorByType( VisualTreeHelper.GetParent( element ), type, specificTypeOnly );
|
||||
return FindAncestorByType(VisualTreeHelper.GetParent(element), type, specificTypeOnly);
|
||||
}
|
||||
|
||||
public static T FindAncestorByType<T>(DependencyObject depObj) where T : DependencyObject
|
||||
{
|
||||
if (depObj == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
if (depObj is T)
|
||||
{
|
||||
return (T) depObj;
|
||||
}
|
||||
|
||||
T parent = default(T);
|
||||
|
||||
parent = FindAncestorByType<T>(VisualTreeHelper.GetParent(depObj));
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByName(UIElement element, string name)
|
||||
{
|
||||
if (element != null && (element is FrameworkElement) && (element as FrameworkElement).Name == name)
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).InvalidateArrange();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantByName(visual, name);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByType(UIElement element, Type type)
|
||||
{
|
||||
return FindDescendantByType(element, type, true);
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByType(UIElement element, Type type, bool specificTypeOnly)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if (element.GetType() == type)
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).InvalidateArrange();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantByType(visual, type, specificTypeOnly);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static T FindDescendantByType<T>(UIElement element) where T : UIElement
|
||||
{
|
||||
UIElement temp = FindDescendantByType(element, typeof (T));
|
||||
|
||||
return (T) temp;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantWithPropertyValue(UIElement element,
|
||||
DependencyProperty dp, object value)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
if (element.GetValue(dp).Equals(value))
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if (element is FrameworkElement)
|
||||
(element as FrameworkElement).InvalidateArrange();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantWithPropertyValue(visual, dp, value);
|
||||
if (foundElement != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
#region Find descendants of type
|
||||
|
||||
public static IEnumerable<T> FindDescendantsOfType<T>(this UIElement element) where T : class
|
||||
{
|
||||
if (element == null) yield break;
|
||||
if (element is T)
|
||||
yield return element as T;
|
||||
|
||||
var frameworkElement = element as FrameworkElement;
|
||||
if (frameworkElement != null)
|
||||
frameworkElement.InvalidateArrange();
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
if (visual == null) continue;
|
||||
foreach (var item in visual.FindDescendantsOfType<T>())
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static T FindAncestorByType<T>( DependencyObject depObj ) where T : DependencyObject
|
||||
{
|
||||
if( depObj == null )
|
||||
{
|
||||
return default( T );
|
||||
}
|
||||
if( depObj is T )
|
||||
{
|
||||
return ( T )depObj;
|
||||
}
|
||||
|
||||
T parent = default( T );
|
||||
|
||||
parent = FindAncestorByType<T>( VisualTreeHelper.GetParent( depObj ) );
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByName(UIElement element, string name)
|
||||
{
|
||||
if( element != null && ( element is FrameworkElement ) && ( element as FrameworkElement ).Name == name )
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).InvalidateArrange();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantByName( visual, name );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByType( UIElement element, Type type )
|
||||
{
|
||||
return FindDescendantByType( element, type, true );
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantByType(UIElement element, Type type, bool specificTypeOnly)
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
|
||||
if( element.GetType() == type )
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).InvalidateArrange();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantByType( visual, type, specificTypeOnly );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static T FindDescendantByType<T>(UIElement element) where T : UIElement
|
||||
{
|
||||
UIElement temp = FindDescendantByType(element, typeof(T));
|
||||
|
||||
return ( T )temp;
|
||||
}
|
||||
|
||||
public static UIElement FindDescendantWithPropertyValue(UIElement element,
|
||||
DependencyProperty dp, object value )
|
||||
{
|
||||
if( element == null )
|
||||
return null;
|
||||
|
||||
if( element.GetValue( dp ).Equals( value ) )
|
||||
return element;
|
||||
|
||||
UIElement foundElement = null;
|
||||
if( element is FrameworkElement )
|
||||
( element as FrameworkElement ).InvalidateArrange();
|
||||
|
||||
for( int i = 0; i < VisualTreeHelper.GetChildrenCount( element ); i++ )
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as UIElement;
|
||||
foundElement = FindDescendantWithPropertyValue( visual, dp, value );
|
||||
if( foundElement != null )
|
||||
break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,14 +129,17 @@
|
|||
<Compile Include="Controls\EdgeControl.cs" />
|
||||
<Compile Include="Controls\EdgeLabels\EdgeLabelControl.cs" />
|
||||
<Compile Include="Controls\EdgePointers\EdgePointerImage.cs" />
|
||||
<Compile Include="Controls\EdgePointers\EdgePointerPath.cs" />
|
||||
<Compile Include="Controls\GraphArea.cs" />
|
||||
<Compile Include="Controls\GraphAreaBase.cs" />
|
||||
<Compile Include="Controls\Misc\IEdgeLabelControl.cs" />
|
||||
<Compile Include="Controls\Misc\IEdgePointer.cs" />
|
||||
<Compile Include="Controls\Misc\IPositionChangeNotify.cs" />
|
||||
<Compile Include="Controls\Misc\ITrackableContent.cs" />
|
||||
<Compile Include="Controls\Misc\IVertexConnectionPoint.cs" />
|
||||
<Compile Include="Controls\Misc\IVertexLabelControl.cs" />
|
||||
<Compile Include="Controls\Misc\IZoomControl.cs" />
|
||||
<Compile Include="Controls\VertexConnectionPoints\StaticVertexConnectionPoint.cs" />
|
||||
<Compile Include="Controls\VertexControl.cs" />
|
||||
<Compile Include="Controls\VertexLabels\VertexLabelControl.cs" />
|
||||
<Compile Include="Controls\ZoomControl\Converters\DoubleToLog10Converter.cs" />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace GraphX.METRO.Controls
|
||||
|
@ -114,5 +115,10 @@ namespace GraphX.METRO.Controls
|
|||
{
|
||||
return new Rect(rect.Left, rect.Top, rect.Width, rect.Height);
|
||||
}
|
||||
|
||||
public static Point Center(this Rect rect)
|
||||
{
|
||||
return new Point(rect.X + rect.Width * .5, rect.Y + rect.Height * .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
Rectangle,
|
||||
Diamond,
|
||||
Triangle,
|
||||
Circle
|
||||
Circle,
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<RootNamespace>GraphX.PCL.Common</RootNamespace>
|
||||
<AssemblyName>GraphX.PCL.Common</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Profile147</TargetFrameworkProfile>
|
||||
<TargetFrameworkProfile>Profile158</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
|
@ -74,6 +74,7 @@
|
|||
<Compile Include="Interfaces\ICore.cs" />
|
||||
<Compile Include="Interfaces\IEdgeRoutingAlgorithm.cs" />
|
||||
<Compile Include="Interfaces\IEdgeRoutingParameters.cs" />
|
||||
<Compile Include="Interfaces\ILayoutEdgeRouting.cs" />
|
||||
<Compile Include="Interfaces\IExternalEdgeRouting.cs" />
|
||||
<Compile Include="Interfaces\IExternalLayout.cs" />
|
||||
<Compile Include="Interfaces\IExternalOverlapRemoval.cs" />
|
||||
|
|
|
@ -40,6 +40,10 @@ namespace GraphX.PCL.Common.Interfaces
|
|||
IOverlapRemovalParameters DefaultOverlapRemovalAlgorithmParams { get; set; }
|
||||
IEdgeRoutingParameters DefaultEdgeRoutingAlgorithmParams { get; set; }
|
||||
|
||||
IExternalLayout<TVertex> ExternalLayoutAlgorithm { get; set; }
|
||||
IExternalOverlapRemoval<TVertex> ExternalOverlapRemovalAlgorithm { get; set; }
|
||||
IExternalEdgeRouting<TVertex, TEdge> ExternalEdgeRoutingAlgorithm { get; set; }
|
||||
|
||||
void ComputeEdgeRoutesByVertex(TVertex dataVertex, Point? vertexPosition = null, Size? vertexSize = null);
|
||||
void CreateNewAlgorithmFactory();
|
||||
void CreateNewAlgorithmStorage(IExternalLayout<TVertex> layout, IExternalOverlapRemoval<TVertex> or, IExternalEdgeRouting<TVertex, TEdge> er);
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
using GraphX.PCL.Common.Models;
|
||||
|
||||
namespace GraphX.PCL.Common.Interfaces
|
||||
namespace GraphX.PCL.Common.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Core vertex data interface
|
||||
/// Core GraphX edge data interface
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">Vertex data type</typeparam>
|
||||
public interface IGraphXEdge<TVertex> : IWeightedEdge<TVertex>, IIdentifiableGraphDataObject, IRoutingInfo
|
||||
public interface IGraphXEdge<TVertex> : IGraphXCommonEdge, IWeightedEdge<TVertex>
|
||||
{
|
||||
/// <summary>
|
||||
/// Source vertex
|
||||
|
@ -16,9 +14,25 @@ namespace GraphX.PCL.Common.Interfaces
|
|||
/// Target vertex
|
||||
/// </summary>
|
||||
new TVertex Target { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core edge data interface
|
||||
/// </summary>
|
||||
public interface IGraphXCommonEdge: IIdentifiableGraphDataObject, IRoutingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// If edge is self-looped
|
||||
/// </summary>
|
||||
bool IsSelfLoop { get; }
|
||||
/// <summary>
|
||||
/// Optional parameter to bind edge to static vertex connection point
|
||||
/// </summary>
|
||||
int? SourceConnectionPointId { get; }
|
||||
/// <summary>
|
||||
/// Optional parameter to bind edge to static vertex connection point
|
||||
/// </summary>
|
||||
int? TargetConnectionPointId { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using GraphX.Measure;
|
||||
|
||||
namespace GraphX.PCL.Common.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// If added to layout algorithm specifies that it uses it's own edge routing and thus
|
||||
/// should ignore edge routing algorithm
|
||||
/// </summary>
|
||||
/// <typeparam name="TEdge">Edge type</typeparam>
|
||||
public interface ILayoutEdgeRouting<TEdge>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get resulting edge routes collection
|
||||
/// </summary>
|
||||
IDictionary<TEdge, Point[]> EdgeRoutes { get; }
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ namespace GraphX.PCL.Common.Interfaces
|
|||
{
|
||||
public interface IWeightedEdge<TVertex> : IEdge<TVertex>
|
||||
{
|
||||
/// <summary>
|
||||
/// Edge weight that can be used by some weight-related layout algorithms
|
||||
/// </summary>
|
||||
double Weight { get; set; }
|
||||
}
|
||||
}
|
|
@ -32,22 +32,39 @@ namespace GraphX.PCL.Common.Models
|
|||
/// <summary>
|
||||
/// Returns true if Source vertex equals Target vertex
|
||||
/// </summary>
|
||||
//[YAXDontSerialize]
|
||||
public bool IsSelfLoop
|
||||
{
|
||||
get { return Source.Equals(Target); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optional parameter to bind edge to static vertex connection point
|
||||
/// </summary>
|
||||
public int? SourceConnectionPointId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional parameter to bind edge to static vertex connection point
|
||||
/// </summary>
|
||||
public int? TargetConnectionPointId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Routing points collection used to make Path visual object
|
||||
/// </summary>
|
||||
//[YAXCustomSerializer(typeof(YAXPointArraySerializer))]
|
||||
public virtual Point[] RoutingPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Source vertex
|
||||
/// </summary>
|
||||
public TVertex Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target vertex
|
||||
/// </summary>
|
||||
public TVertex Target { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edge weight that can be used by some weight-related layout algorithms
|
||||
/// </summary>
|
||||
public double Weight { get; set; }
|
||||
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
if (parameters is PathFinderEdgeRoutingParameters)
|
||||
{
|
||||
var prms = parameters as PathFinderEdgeRoutingParameters;
|
||||
_horizontalGS = prms.HorizontalGridSize;
|
||||
_verticalGS = prms.VerticalGridSize;
|
||||
_horizontalGs = prms.HorizontalGridSize;
|
||||
_verticalGs = prms.VerticalGridSize;
|
||||
_sideAreaOffset = prms.SideGridOffset;
|
||||
_useDiagonals = prms.UseDiagonals;
|
||||
_pathAlgo = prms.PathFinderAlgorithm;
|
||||
|
@ -41,8 +41,8 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
|
||||
public override Point[] ComputeSingle(TEdge edge)
|
||||
{
|
||||
calculateMatrix(CancellationToken.None);//maybe shouldnt do this cause can be used from algo storage and already inited
|
||||
setupPathFinder();//
|
||||
CalculateMatrix(CancellationToken.None);//maybe shouldnt do this cause can be used from algo storage and already inited
|
||||
SetupPathFinder();//
|
||||
ComputeER(edge, CancellationToken.None);
|
||||
return EdgeRoutes.ContainsKey(edge) ? EdgeRoutes[edge] : null;
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
|
||||
EdgeRoutes.Clear();
|
||||
|
||||
calculateMatrix(cancellationToken);
|
||||
setupPathFinder();
|
||||
CalculateMatrix(cancellationToken);
|
||||
SetupPathFinder();
|
||||
|
||||
|
||||
foreach (var item in Graph.Edges)
|
||||
|
@ -72,8 +72,8 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
|
||||
private Point _minPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
|
||||
private Point _maxPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
|
||||
private double _horizontalGS = 100;
|
||||
private double _verticalGS = 100;
|
||||
private double _horizontalGs = 100;
|
||||
private double _verticalGs = 100;
|
||||
private double _sideAreaOffset = 500;
|
||||
private bool _useDiagonals = true;
|
||||
private double _vertexSafeDistance = 30;
|
||||
|
@ -84,41 +84,41 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
private int _searchLimit = 50000;
|
||||
private PathFindAlgorithm _pathAlgo = PathFindAlgorithm.Manhattan;
|
||||
|
||||
private MatrixItem[,] resMatrix;
|
||||
private List<MatrixItem> validPoints;
|
||||
private PathFinder pathFinder;
|
||||
private MatrixItem[,] _resMatrix;
|
||||
private List<MatrixItem> _validPoints;
|
||||
private PathFinder _pathFinder;
|
||||
|
||||
|
||||
#region Setup pathfinder
|
||||
private void setupPathFinder()
|
||||
private void SetupPathFinder()
|
||||
{
|
||||
pathFinder = new PathFinder(resMatrix);
|
||||
pathFinder.Diagonals = _useDiagonals;
|
||||
pathFinder.HeuristicEstimate = _pfHeuristic;
|
||||
pathFinder.HeavyDiagonals = _useHeavyDiagonals;
|
||||
pathFinder.PunishChangeDirection = _punishChangeDirection;
|
||||
pathFinder.TieBreaker = _useTieBreaker;
|
||||
pathFinder.SearchLimit = _searchLimit;
|
||||
_pathFinder = new PathFinder(_resMatrix);
|
||||
_pathFinder.Diagonals = _useDiagonals;
|
||||
_pathFinder.HeuristicEstimate = _pfHeuristic;
|
||||
_pathFinder.HeavyDiagonals = _useHeavyDiagonals;
|
||||
_pathFinder.PunishChangeDirection = _punishChangeDirection;
|
||||
_pathFinder.TieBreaker = _useTieBreaker;
|
||||
_pathFinder.SearchLimit = _searchLimit;
|
||||
|
||||
switch (_pathAlgo)
|
||||
{
|
||||
case PathFindAlgorithm.Manhattan:
|
||||
pathFinder.Formula = HeuristicFormula.Manhattan;
|
||||
_pathFinder.Formula = HeuristicFormula.Manhattan;
|
||||
break;
|
||||
case PathFindAlgorithm.MaxDXDY:
|
||||
pathFinder.Formula = HeuristicFormula.MaxDXDY;
|
||||
_pathFinder.Formula = HeuristicFormula.MaxDXDY;
|
||||
break;
|
||||
case PathFindAlgorithm.Euclidean:
|
||||
pathFinder.Formula = HeuristicFormula.Euclidean;
|
||||
_pathFinder.Formula = HeuristicFormula.Euclidean;
|
||||
break;
|
||||
case PathFindAlgorithm.EuclideanNoSQR:
|
||||
pathFinder.Formula = HeuristicFormula.EuclideanNoSQR;
|
||||
_pathFinder.Formula = HeuristicFormula.EuclideanNoSQR;
|
||||
break;
|
||||
case PathFindAlgorithm.DiagonalShortCut:
|
||||
pathFinder.Formula = HeuristicFormula.DiagonalShortCut;
|
||||
_pathFinder.Formula = HeuristicFormula.DiagonalShortCut;
|
||||
break;
|
||||
case PathFindAlgorithm.Custom1:
|
||||
pathFinder.Formula = HeuristicFormula.Custom1;
|
||||
_pathFinder.Formula = HeuristicFormula.Custom1;
|
||||
break;
|
||||
default: throw new Exception("setupPathFinder() -> Unknown formula!");
|
||||
}
|
||||
|
@ -126,16 +126,16 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
#endregion
|
||||
|
||||
#region Calculate matrix
|
||||
private void calculateMatrix(CancellationToken cancellationToken)
|
||||
private void CalculateMatrix(CancellationToken cancellationToken)
|
||||
{
|
||||
var tl = new Point(_minPoint.X - _sideAreaOffset, _minPoint.Y - _sideAreaOffset);
|
||||
var br = new Point(_maxPoint.X + _sideAreaOffset, _maxPoint.Y + _sideAreaOffset);
|
||||
|
||||
var hCount = (int)((br.X - tl.X) / _horizontalGS) + 1;
|
||||
var vCount = (int)((br.Y - tl.Y) / _verticalGS) + 1;
|
||||
var hCount = (int)((br.X - tl.X) / _horizontalGs) + 1;
|
||||
var vCount = (int)((br.Y - tl.Y) / _verticalGs) + 1;
|
||||
|
||||
resMatrix = new MatrixItem[hCount, vCount];
|
||||
validPoints = new List<MatrixItem>();
|
||||
_resMatrix = new MatrixItem[hCount, vCount];
|
||||
_validPoints = new List<MatrixItem>();
|
||||
|
||||
var lastPt = new Point(0, 0);
|
||||
|
||||
|
@ -145,9 +145,9 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
lastPt = new Point(tl.X + _horizontalGS * i, tl.Y + _verticalGS * j);
|
||||
resMatrix[i, j] = new MatrixItem(lastPt, IsOverlapped(lastPt), i, j);
|
||||
if (!resMatrix[i, j].IsIntersected) validPoints.Add(resMatrix[i, j]);
|
||||
lastPt = new Point(tl.X + _horizontalGs * i, tl.Y + _verticalGs * j);
|
||||
_resMatrix[i, j] = new MatrixItem(lastPt, IsOverlapped(lastPt), i, j);
|
||||
if (!_resMatrix[i, j].IsIntersected) _validPoints.Add(_resMatrix[i, j]);
|
||||
}
|
||||
////////////debug
|
||||
#if DEBUG
|
||||
|
@ -156,7 +156,7 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
var str = "";
|
||||
for (int j = 0; j < hCount; j++)
|
||||
{
|
||||
str += resMatrix[j, i].IsIntersected ? "0 " : "1 ";
|
||||
str += _resMatrix[j, i].IsIntersected ? "0 " : "1 ";
|
||||
}
|
||||
Debug.WriteLine(str);
|
||||
}
|
||||
|
@ -167,9 +167,9 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
|
||||
private void ComputeER(TEdge item, CancellationToken cancellationToken)
|
||||
{
|
||||
var startPt = getClosestPoint(validPoints, VertexPositions[item.Source]);
|
||||
var endPt = getClosestPoint(validPoints, VertexPositions[item.Target]);
|
||||
var lst = pathFinder.FindPath(startPt, endPt);
|
||||
var startPt = GetClosestPoint(_validPoints, VertexPositions[item.Source]);
|
||||
var endPt = GetClosestPoint(_validPoints, VertexPositions[item.Target]);
|
||||
var lst = _pathFinder.FindPath(startPt, endPt);
|
||||
|
||||
if (lst == null) return;
|
||||
var ptlst = new List<Point>();
|
||||
|
@ -177,7 +177,7 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var mi = resMatrix[pt.X, pt.Y];
|
||||
var mi = _resMatrix[pt.X, pt.Y];
|
||||
ptlst.Add(mi.Point);
|
||||
}
|
||||
if (EdgeRoutes.ContainsKey(item))
|
||||
|
@ -188,25 +188,19 @@ namespace GraphX.PCL.Logic.Algorithms.EdgeRouting
|
|||
private bool IsOverlapped(Point pt)
|
||||
{
|
||||
var trect = new Rect(pt.X, pt.Y, 2, 2);
|
||||
foreach (var item in VertexSizes)
|
||||
{
|
||||
var rect = new Rect(item.Value.X - _vertexSafeDistance, item.Value.Y - _vertexSafeDistance, item.Value.Width + _vertexSafeDistance, item.Value.Height + _vertexSafeDistance);
|
||||
if (rect.IntersectsWith(trect))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return VertexSizes.Select(item => new Rect(item.Value.X - _vertexSafeDistance, item.Value.Y - _vertexSafeDistance, item.Value.Width + _vertexSafeDistance, item.Value.Height + _vertexSafeDistance)).Any(rect => rect.IntersectsWith(trect));
|
||||
}
|
||||
|
||||
private Point getClosestPoint(List<MatrixItem> points, Point pt)
|
||||
private Point GetClosestPoint(IEnumerable<MatrixItem> points, Point pt)
|
||||
{
|
||||
var lst = points.Where(mi => mi.Point != pt).
|
||||
OrderBy(mi => getFakeDistance(pt, mi.Point)).
|
||||
OrderBy(mi => GetFakeDistance(pt, mi.Point)).
|
||||
Take(1);
|
||||
if (lst.Count() == 0) throw new Exception("GetClosestPoint() -> Can't find one!");
|
||||
if (!lst.Any()) throw new Exception("GetClosestPoint() -> Can't find one!");
|
||||
return new Point(lst.First().PlaceX, lst.First().PlaceY);
|
||||
}
|
||||
|
||||
private double getFakeDistance(Point source, Point target)
|
||||
private double GetFakeDistance(Point source, Point target)
|
||||
{
|
||||
double dx = target.X - source.X; double dy = target.Y - source.Y;
|
||||
return dx * dx + dy * dy;
|
||||
|
|
|
@ -45,12 +45,10 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
|||
CalculateHorizontalPositions(LeftRightMode.Right, UpperLowerEdges.Lower);
|
||||
|
||||
CalculateRealPositions();
|
||||
DoEdgeRouting();
|
||||
|
||||
SavePositions();
|
||||
}
|
||||
|
||||
private void DoEdgeRouting()
|
||||
private void DoEdgeRouting(double offsetY)
|
||||
{
|
||||
switch (Parameters.EdgeRouting)
|
||||
{
|
||||
|
@ -58,7 +56,7 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
|||
DoTraditionalEdgeRouting();
|
||||
break;
|
||||
case SugiyamaEdgeRoutings.Orthogonal:
|
||||
DoOrthogonalEdgeRouting();
|
||||
DoOrthogonalEdgeRouting(offsetY);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -66,9 +64,40 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
|||
|
||||
}
|
||||
|
||||
private void DoOrthogonalEdgeRouting()
|
||||
private void DoOrthogonalEdgeRouting(double offsetY)
|
||||
{
|
||||
foreach (var edge in VisitedGraph.Edges)
|
||||
{
|
||||
var sourcePosition = VertexPositions[edge.Source];
|
||||
var targetPosition = VertexPositions[edge.Target];
|
||||
var sourceSize = _vertexSizes[edge.Source];
|
||||
var targetSize = _vertexSizes[edge.Target];
|
||||
|
||||
if ((Parameters.Direction == LayoutDirection.TopToBottom || Parameters.Direction == LayoutDirection.BottomToTop) && sourcePosition.X != targetPosition.X)
|
||||
{
|
||||
_edgeRoutingPoints[edge] =
|
||||
new[]
|
||||
{
|
||||
new Point(0, 0),
|
||||
new Point(targetPosition.X + targetSize.Width / 2, sourcePosition.Y + sourceSize.Height / 2),
|
||||
new Point(0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
if ((Parameters.Direction == LayoutDirection.LeftToRight || Parameters.Direction == LayoutDirection.RightToLeft) && sourcePosition.Y != targetPosition.Y)
|
||||
{
|
||||
_edgeRoutingPoints[edge] =
|
||||
new[]
|
||||
{
|
||||
new Point(0, 0),
|
||||
new Point(sourcePosition.X + sourceSize.Width /2, targetPosition.Y + targetSize.Height / 2),
|
||||
new Point(0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/* foreach (var edge in VisitedGraph.Edges)
|
||||
{
|
||||
Point[] orthoRoutePoints = new Point[2];
|
||||
var sourceVertex = _vertexMap[edge.Source];
|
||||
|
@ -105,7 +134,7 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
|||
routePoints[kvp.Value.Count + 2] = new Point(routePoints[kvp.Value.Count + 1].X, routePoints[kvp.Value.Count + 3].Y);
|
||||
_edgeRoutingPoints[kvp.Key] = routePoints;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private void DoTraditionalEdgeRouting()
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using GraphX.Measure;
|
||||
using GraphX.PCL.Common.Interfaces;
|
||||
using QuickGraph;
|
||||
|
||||
namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
||||
{
|
||||
public partial class EfficientSugiyamaLayoutAlgorithm<TVertex, TEdge, TGraph>
|
||||
: DefaultParameterizedLayoutAlgorithmBase<TVertex, TEdge, TGraph, EfficientSugiyamaLayoutParameters>/*,
|
||||
IEdgeRoutingAlgorithm<TVertex, TEdge>*/
|
||||
: DefaultParameterizedLayoutAlgorithmBase<TVertex, TEdge, TGraph, EfficientSugiyamaLayoutParameters>,
|
||||
ILayoutEdgeRouting<TEdge>
|
||||
where TVertex : class
|
||||
where TEdge : IEdge<TVertex>
|
||||
where TGraph : IVertexAndEdgeListGraph<TVertex, TEdge>
|
||||
|
@ -105,11 +107,39 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
|
|||
BuildSparseNormalizedGraph(cancellationToken);
|
||||
DoCrossingMinimizations(cancellationToken);
|
||||
CalculatePositions();
|
||||
var offsetY = 0d;
|
||||
if (Parameters.Direction == LayoutDirection.LeftToRight)
|
||||
{
|
||||
offsetY = VertexPositions.Values.Min(p => p.X);
|
||||
if (offsetY < 0) offsetY = -offsetY;
|
||||
foreach (var item in VertexPositions.ToDictionary(a=> a.Key, b=> b.Value))
|
||||
{
|
||||
VertexPositions[item.Key] = new Point(item.Value.Y * 1.5 + 0, item.Value.X + offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
if(Parameters.Direction == LayoutDirection.RightToLeft)
|
||||
{
|
||||
offsetY = VertexPositions.Values.Min(p => p.X);
|
||||
if (offsetY < 0) offsetY = -offsetY;
|
||||
foreach (var item in VertexPositions.ToDictionary(a => a.Key, b => b.Value))
|
||||
{
|
||||
VertexPositions[item.Key] = new Point(-item.Value.Y * 1.5, -item.Value.X + offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
if (Parameters.Direction == LayoutDirection.BottomToTop)
|
||||
{
|
||||
foreach (var item in VertexPositions.ToDictionary(a => a.Key, b => b.Value))
|
||||
{
|
||||
VertexPositions[item.Key] = new Point(item.Value.X, -item.Value.Y);
|
||||
}
|
||||
}
|
||||
|
||||
DoEdgeRouting(offsetY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IEdgeRoutingAlgorithm<TVertex,TEdge,TGraph> Members
|
||||
#region ILayoutEdgeRouting<TEdge> Members
|
||||
|
||||
public IDictionary<TEdge, Point[]> EdgeRoutes
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
public class EfficientSugiyamaLayoutParameters : LayoutParametersBase
|
||||
{
|
||||
//private LayoutDirection _direction = LayoutDirection.TopToBottom;
|
||||
private LayoutDirection _direction = LayoutDirection.TopToBottom;
|
||||
private double _layerDistance = 15.0;
|
||||
private double _vertexDistance = 15.0;
|
||||
private int _positionMode = -1;
|
||||
|
@ -12,8 +12,10 @@
|
|||
internal const int MAX_PERMUTATIONS = 50;
|
||||
private SugiyamaEdgeRoutings _edgeRouting = SugiyamaEdgeRoutings.Traditional;
|
||||
|
||||
//it will be available on next releases
|
||||
/*public LayoutDirection Direction
|
||||
/// <summary>
|
||||
/// Layout direction
|
||||
/// </summary>
|
||||
public LayoutDirection Direction
|
||||
{
|
||||
get { return _direction; }
|
||||
set
|
||||
|
@ -24,7 +26,7 @@
|
|||
_direction = value;
|
||||
NotifyPropertyChanged("Direction");
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public double LayerDistance
|
||||
{
|
||||
|
|
|
@ -30,17 +30,17 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
|
||||
protected override void RemoveOverlap(CancellationToken cancellationToken)
|
||||
{
|
||||
DateTime t0 = DateTime.Now;
|
||||
//DateTime t0 = DateTime.Now;
|
||||
double cost = HorizontalImproved(cancellationToken);
|
||||
DateTime t1 = DateTime.Now;
|
||||
//DateTime t1 = DateTime.Now;
|
||||
|
||||
Debug.WriteLine( "PFS horizontal: cost=" + cost + " time=" + ( t1 - t0 ) );
|
||||
//Debug.WriteLine( "PFS horizontal: cost=" + cost + " time=" + ( t1 - t0 ) );
|
||||
|
||||
t1 = DateTime.Now;
|
||||
//t1 = DateTime.Now;
|
||||
cost = VerticalImproved(cancellationToken);
|
||||
DateTime t2 = DateTime.Now;
|
||||
Debug.WriteLine( "PFS vertical: cost=" + cost + " time=" + ( t2 - t1 ) );
|
||||
Debug.WriteLine( "PFS total: time=" + ( t2 - t0 ) );
|
||||
// DateTime t2 = DateTime.Now;
|
||||
// Debug.WriteLine( "PFS vertical: cost=" + cost + " time=" + ( t2 - t1 ) );
|
||||
// Debug.WriteLine( "PFS total: time=" + ( t2 - t0 ) );
|
||||
}
|
||||
|
||||
protected Vector Force( Rect vi, Rect vj )
|
||||
|
@ -66,7 +66,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
return f;
|
||||
}
|
||||
|
||||
protected Vector force2( Rect vi, Rect vj )
|
||||
protected Vector Force2( Rect vi, Rect vj )
|
||||
{
|
||||
var f = new Vector( 0, 0 );
|
||||
Vector d = vj.GetCenter() - vi.GetCenter();
|
||||
|
@ -102,6 +102,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
protected void Horizontal(CancellationToken cancellationToken)
|
||||
{
|
||||
WrappedRectangles.Sort( XComparison );
|
||||
|
@ -144,14 +145,14 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
for ( int j = k + 1; j < n; j++ )
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
RectangleWrapper<TObject> r = WrappedRectangles[j];
|
||||
var r = WrappedRectangles[j];
|
||||
r.Rectangle.Offset( delta, 0 );
|
||||
}
|
||||
i = k + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
protected double HorizontalImproved(CancellationToken cancellationToken)
|
||||
{
|
||||
if (WrappedRectangles.Count == 0) return 0;
|
||||
|
@ -159,20 +160,20 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
int i = 0, n = WrappedRectangles.Count;
|
||||
|
||||
//bal szelso
|
||||
RectangleWrapper<TObject> lmin = WrappedRectangles[0];
|
||||
var lmin = WrappedRectangles[0];
|
||||
double sigma = 0, x0 = lmin.CenterX;
|
||||
var gamma = new double[WrappedRectangles.Count];
|
||||
var x = new double[WrappedRectangles.Count];
|
||||
while ( i < n )
|
||||
{
|
||||
RectangleWrapper<TObject> u = WrappedRectangles[i];
|
||||
var u = WrappedRectangles[i];
|
||||
|
||||
//i-vel azonos középponttal rendelkezõ téglalapok meghatározása
|
||||
int k = i;
|
||||
for ( int j = i + 1; j < n; j++ )
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
RectangleWrapper<TObject> v = WrappedRectangles[j];
|
||||
var v = WrappedRectangles[j];
|
||||
if ( u.CenterX == v.CenterX )
|
||||
{
|
||||
u = v;
|
||||
|
@ -197,7 +198,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
Vector f = Force( WrappedRectangles[j].Rectangle, WrappedRectangles[m].Rectangle );
|
||||
ggg = Math.Max( f.X + gamma[j], ggg );
|
||||
}
|
||||
RectangleWrapper<TObject> v = WrappedRectangles[m];
|
||||
var v = WrappedRectangles[m];
|
||||
double gg =
|
||||
v.Rectangle.Left + ggg < lmin.Rectangle.Left
|
||||
? sigma
|
||||
|
@ -242,7 +243,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
RectangleWrapper<TObject> r = WrappedRectangles[i];
|
||||
var r = WrappedRectangles[i];
|
||||
double oldPos = r.Rectangle.Left;
|
||||
double newPos = x[i];
|
||||
|
||||
|
@ -269,7 +270,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
protected void Vertical(CancellationToken cancellationToken)
|
||||
{
|
||||
WrappedRectangles.Sort( YComparison );
|
||||
|
@ -302,7 +303,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Vector f = force2( WrappedRectangles[m].Rectangle, WrappedRectangles[j].Rectangle );
|
||||
Vector f = Force2( WrappedRectangles[m].Rectangle, WrappedRectangles[j].Rectangle );
|
||||
if ( f.Y > delta )
|
||||
{
|
||||
delta = f.Y;
|
||||
|
@ -320,7 +321,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
protected double VerticalImproved(CancellationToken cancellationToken)
|
||||
{
|
||||
if (WrappedRectangles.Count == 0) return 0;
|
||||
|
@ -359,7 +360,7 @@ namespace GraphX.PCL.Logic.Algorithms.OverlapRemoval
|
|||
double ggg = 0;
|
||||
for ( int j = 0; j < i; j++ )
|
||||
{
|
||||
Vector f = force2( WrappedRectangles[j].Rectangle, WrappedRectangles[m].Rectangle );
|
||||
Vector f = Force2( WrappedRectangles[j].Rectangle, WrappedRectangles[m].Rectangle );
|
||||
ggg = Math.Max( f.Y + gamma[j], ggg );
|
||||
}
|
||||
RectangleWrapper<TObject> v = WrappedRectangles[m];
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<RootNamespace>GraphX.PCL.Logic</RootNamespace>
|
||||
<AssemblyName>GraphX.PCL.Logic</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Profile147</TargetFrameworkProfile>
|
||||
<TargetFrameworkProfile>Profile136</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="QuickGraph" version="3.6.61119.7" targetFramework="net40-Client" />
|
||||
<package id="QuickGraph" version="3.6.61119.7" targetFramework="net40-Client" requireReinstallation="True" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче