All the controls missing in WPF. Over 1 million downloads.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

384 lines
10 KiB

/*************************************************************************************
Extended WPF Toolkit
Copyright (C) 2007-2013 Xceed Software Inc.
This program is provided to you under the terms of the Microsoft Public
License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
For more features, controls, and fast professional support,
pick up the Plus Edition at http://xceed.com/wpf_toolkit
Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
***********************************************************************************/
using System;
using System.Windows;
namespace Xceed.Wpf.Toolkit.Core.Utilities
{
internal struct Segment
{
#region Constructors
public Segment( Point point )
{
_p1 = point;
_p2 = point;
_isP1Excluded = false;
_isP2Excluded = false;
}
public Segment( Point p1, Point p2 )
{
_p1 = p1;
_p2 = p2;
_isP1Excluded = false;
_isP2Excluded = false;
}
public Segment( Point p1, Point p2, bool excludeP1, bool excludeP2 )
{
_p1 = p1;
_p2 = p2;
_isP1Excluded = excludeP1;
_isP2Excluded = excludeP2;
}
#endregion
#region Empty Static Properties
public static Segment Empty
{
get
{
Segment result = new Segment( new Point( 0, 0 ) );
result._isP1Excluded = true;
result._isP2Excluded = true;
return result;
}
}
#endregion
#region P1 Property
public Point P1
{
get
{
return _p1;
}
}
#endregion
#region P2 Property
public Point P2
{
get
{
return _p2;
}
}
#endregion
#region IsP1Excluded Property
public bool IsP1Excluded
{
get
{
return _isP1Excluded;
}
}
#endregion
#region IsP2Excluded Property
public bool IsP2Excluded
{
get
{
return _isP2Excluded;
}
}
#endregion
#region IsEmpty Property
public bool IsEmpty
{
get
{
return DoubleHelper.AreVirtuallyEqual( _p1, _p2 ) && ( _isP1Excluded || _isP2Excluded );
}
}
#endregion
#region IsPoint Property
public bool IsPoint
{
get
{
return DoubleHelper.AreVirtuallyEqual( _p1, _p2 );
}
}
#endregion
#region Length Property
public double Length
{
get
{
return ( this.P2 - this.P1 ).Length;
}
}
#endregion
#region Slope Property
public double Slope
{
get
{
return ( this.P2.X == this.P1.X ) ? double.NaN : ( this.P2.Y - this.P1.Y ) / ( this.P2.X - this.P1.X );
}
}
#endregion
public bool Contains( Point point )
{
if( IsEmpty )
return false;
// if the point is an endpoint, ensure that it is not excluded
if( DoubleHelper.AreVirtuallyEqual( _p1, point ) )
return _isP1Excluded;
if( DoubleHelper.AreVirtuallyEqual( _p2, point ) )
return _isP2Excluded;
bool result = false;
// ensure that a line through P1 and the point is parallel to the current segment
if( DoubleHelper.AreVirtuallyEqual( Slope, new Segment( _p1, point ).Slope ) )
{
// finally, ensure that the point is between the segment's endpoints
result = ( point.X >= Math.Min( _p1.X, _p2.X ) )
&& ( point.X <= Math.Max( _p1.X, _p2.X ) )
&& ( point.Y >= Math.Min( _p1.Y, _p2.Y ) )
&& ( point.Y <= Math.Max( _p1.Y, _p2.Y ) );
}
return result;
}
public bool Contains( Segment segment )
{
return ( segment == this.Intersection( segment ) );
}
public override bool Equals( object o )
{
if( !( o is Segment ) )
return false;
Segment other = ( Segment )o;
// empty segments are always considered equal
if( this.IsEmpty )
return other.IsEmpty;
// segments are considered equal if
// 1) the endpoints are equal and equally excluded
// 2) the opposing endpoints are equal and equally excluded
if( DoubleHelper.AreVirtuallyEqual( _p1, other._p1 ) )
{
return ( DoubleHelper.AreVirtuallyEqual( _p2, other._p2 )
&& _isP1Excluded == other._isP1Excluded
&& _isP2Excluded == other._isP2Excluded );
}
else
{
return ( DoubleHelper.AreVirtuallyEqual( _p1, other._p2 )
&& DoubleHelper.AreVirtuallyEqual( _p2, other._p1 )
&& _isP1Excluded == other._isP2Excluded
&& _isP2Excluded == other._isP1Excluded );
}
}
public override int GetHashCode()
{
return _p1.GetHashCode() ^ _p2.GetHashCode() ^ _isP1Excluded.GetHashCode() ^ _isP2Excluded.GetHashCode();
}
public Segment Intersection( Segment segment )
{
// if either segment is empty, the intersection is also empty
if( this.IsEmpty || segment.IsEmpty )
return Segment.Empty;
// if the segments are equal, just return a new equal segment
if( this == segment )
return new Segment( this._p1, this._p2, this._isP1Excluded, this._isP2Excluded );
// if either segment is a Point, just see if the point is contained in the other segment
if( this.IsPoint )
return segment.Contains( this._p1 ) ? new Segment( this._p1 ) : Segment.Empty;
if( segment.IsPoint )
return this.Contains( segment._p1 ) ? new Segment( segment._p1 ) : Segment.Empty;
// okay, no easy answer, so let's do the math...
Point p1 = this._p1;
Vector v1 = this._p2 - this._p1;
Point p2 = segment._p1;
Vector v2 = segment._p2 - segment._p1;
Vector endpointVector = p2 - p1;
double xProd = Vector.CrossProduct( v1, v2 );
// if segments are not parallel, then look for intersection on each segment
if( !DoubleHelper.AreVirtuallyEqual( Slope, segment.Slope ) )
{
// check for intersection on other segment
double s = ( Vector.CrossProduct( endpointVector, v1 ) ) / xProd;
if( s < 0 || s > 1 )
return Segment.Empty;
// check for intersection on this segment
s = ( Vector.CrossProduct( endpointVector, v2 ) ) / xProd;
if( s < 0 || s > 1 )
return Segment.Empty;
// intersection of segments is a point
return new Segment( p1 + s * v1 );
}
// segments are parallel
xProd = Vector.CrossProduct( endpointVector, v1 );
if( xProd * xProd > 1.0e-06 * v1.LengthSquared * endpointVector.LengthSquared )
{
// segments do not intersect
return Segment.Empty;
}
// intersection is overlapping segment
Segment result = new Segment();
// to determine the overlapping segment, create reference segments where the endpoints are *not* excluded
Segment refThis = new Segment( this._p1, this._p2 );
Segment refSegment = new Segment( segment._p1, segment._p2 );
// check whether this segment is contained in the other segment
bool includeThisP1 = refSegment.Contains( refThis._p1 );
bool includeThisP2 = refSegment.Contains( refThis._p2 );
if( includeThisP1 && includeThisP2 )
{
result._p1 = this._p1;
result._p2 = this._p2;
result._isP1Excluded = this._isP1Excluded || !segment.Contains( this._p1 );
result._isP2Excluded = this._isP2Excluded || !segment.Contains( this._p2 );
return result;
}
// check whether the other segment is contained in this segment
bool includeSegmentP1 = refThis.Contains( refSegment._p1 );
bool includeSegmentP2 = refThis.Contains( refSegment._p2 );
if( includeSegmentP1 && includeSegmentP2 )
{
result._p1 = segment._p1;
result._p2 = segment._p2;
result._isP1Excluded = segment._isP1Excluded || !this.Contains( segment._p1 );
result._isP2Excluded = segment._isP2Excluded || !this.Contains( segment._p2 );
return result;
}
// the intersection must include one endpoint from this segment and one endpoint from the other segment
if( includeThisP1 )
{
result._p1 = this._p1;
result._isP1Excluded = this._isP1Excluded || !segment.Contains( this._p1 );
}
else
{
result._p1 = this._p2;
result._isP1Excluded = this._isP2Excluded || !segment.Contains( this._p2 );
}
if( includeSegmentP1 )
{
result._p2 = segment._p1;
result._isP2Excluded = segment._isP1Excluded || !this.Contains( segment._p1 );
}
else
{
result._p2 = segment._p2;
result._isP2Excluded = segment._isP2Excluded || !this.Contains( segment._p2 );
}
return result;
}
public override string ToString()
{
string s = base.ToString();
if( this.IsEmpty )
{
s = s + ": {Empty}";
}
else if( this.IsPoint )
{
s = s + ", Point: " + _p1.ToString();
}
else
{
s = s + ": " + _p1.ToString() + ( _isP1Excluded ? " (excl)" : " (incl)" )
+ " to " + _p2.ToString() + ( _isP2Excluded ? " (excl)" : " (incl)" );
}
return s;
}
#region Operators Methods
public static bool operator ==( Segment s1, Segment s2 )
{
if( ( object )s1 == null )
return ( object )s2 == null;
if( ( object )s2 == null )
return ( object )s1 == null;
return s1.Equals( s2 );
}
public static bool operator !=( Segment s1, Segment s2 )
{
return !( s1 == s2 );
}
#endregion
#region Private Fields
private bool _isP1Excluded;
private bool _isP2Excluded;
private Point _p1;
private Point _p2;
#endregion
}
}