Many changes. See changelog.txt

This commit is contained in:
Alexander Smirnov 2015-05-20 02:16:12 +03:00
Родитель e67a8b42fb
Коммит 64163c710a
62 изменённых файлов: 2151 добавлений и 496 удалений

Просмотреть файл

@ -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

Двоичный файл не отображается.

Просмотреть файл

@ -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>