(* ::Package:: *)

(************************************************************************)
(* This file was generated automatically by the Mathematica front end.  *)
(* It contains Initialization cells from a Notebook file, which         *)
(* typically will have the same name as this file except ending in      *)
(* ".nb" instead of ".m".                                               *)
(*                                                                      *)
(* This file is intended to be loaded into the Mathematica kernel using *)
(* the package loading commands Get or Needs.  Doing so is equivalent   *)
(* to using the Evaluate Initialization Cells menu command in the front *)
(* end.                                                                 *)
(*                                                                      *)
(* DO NOT EDIT THIS FILE.  This entire file is regenerated              *)
(* automatically each time the parent Notebook file is saved in the     *)
(* Mathematica front end.  Any changes you make to this file will be    *)
(* overwritten.                                                         *)
(************************************************************************)



(* :Title: FigGeometry *)
(* :Context: SciDraw` *)
(* :Summary: Geometry utilities needed by figure objects *)
(* :Author: Mark A. Caprio, Department of Physics, University of Notre Dame *)
(* :Copyright: Copyright FIGYEAR, Mark A. Caprio *)
(* :Package Version: FIGVERSION *)
(* :Mathematica Version: MATHVERSION *)
(* :Discussion: FIGDISCUSSION *)
(* :History: See main package file. *)


BeginPackage["SciDraw`",SciDraw`Private`$ExternalContexts];


Unprotect[Evaluate[$Context<>"*"]];


Begin["`Private`"];





ScalarParameterPattern=None|(_?NumericQ);
NonNegativeScalarParameterPattern=None|NonNegativePattern;


UpgradeScalar[None]:=0;
UpgradeScalar[x_?NumericQ]:=x;


RescaleInterval[{u1_?NumericQ,u2_?NumericQ},{v1_?NumericQ,v2_?NumericQ}][u_?NumericQ]:=Rescale[u,{u1,u2},{v1,v2}];


InRange[{x1_,x2_},x_]:=(x1<=x)&&(x<=x2);


InRange[{{x1_,x2_},{y1_,y2_}},{x_,y_}]:=InRange[{x1,x2},x]&&InRange[{y1,y2},y];


ExtendInterval[PRange:{_?NumericQ,_?NumericQ},PDiff:{_?NumericQ,_?NumericQ},Mode:Absolute]:=PRange+PDiff*{-1,+1};
ExtendInterval[PRange:{_?NumericQ,_?NumericQ},PFrac:{_?NumericQ,_?NumericQ},Mode:Scaled]:=PRange+PFrac*{-1,+1}*-Subtract@@PRange;


VectorLength[{x_?NumericQ,y_?NumericQ}]:=Sqrt[x^2+y^2];VectorArcTan[{x_?NumericQ,y_?NumericQ}]:=If[{x,y}=={0,0},0.,ArcTan[x,y]];


SegmentLength[{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}}]:=VectorLength[p2-p1];
SegmentTangent[{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}}]:=(p2-p1)/VectorLength[p2-p1];SegmentArcTan[{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}}]:=VectorArcTan[p2-p1];


FromPolar[{rho_,phi_}]:=rho*{Cos[phi],Sin[phi]};


InterpolateSegment[
s:{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}},
Reference:(Tail|Center|Head),Mode:(Absolute|Scaled):Scaled,
x_?NumericQ
]/;(Chop[SegmentLength[s]]==0):=p1;


InterpolateSegment[
s:{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}},
Reference:Tail,
Mode:(Absolute|Scaled):Scaled,
x_?NumericQ
]/;(Chop[SegmentLength[s]]!=0):=p1+x*(p2-p1)/Switch[Mode,Absolute,SegmentLength[s],Scaled,1];
InterpolateSegment[
s:{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}},
Reference:Center,
Mode:(Absolute|Scaled):Scaled,
x_?NumericQ
]/;(Chop[SegmentLength[s]]!=0):=(p2+p1)/2+x*(p2-p1)/Switch[Mode,Absolute,SegmentLength[s],Scaled,2];
InterpolateSegment[
s:{p1:{_?NumericQ,_?NumericQ},p2:{_?NumericQ,_?NumericQ}},
Reference:Head,
Mode:(Absolute|Scaled):Scaled,
x_?NumericQ
]/;(Chop[SegmentLength[s]]!=0):=p2+x*(p2-p1)/Switch[Mode,Absolute,SegmentLength[s],Scaled,1];


NumericalPairPattern={_?NumericQ,_?NumericQ};


UpgradePair[a:NonListPattern]:={a,a};
UpgradePair[{x:NonListPattern,y:NonListPattern}]:={x,y};


IntervalParametersPattern=(None|(_?NumericQ)|{(_?NumericQ),(_?NumericQ)});
NonNegativeIntervalParametersPattern=(None|NonNegativePattern|{NonNegativePattern,NonNegativePattern});


UpgradePairEqual[None]:={0,0};
UpgradePairEqual[x_?NumericQ]:={x,x};
UpgradePairEqual[{x_?NumericQ,y_?NumericQ}]:={x,y};


UpgradePairHorizontal[None]:={0,0};
UpgradePairHorizontal[x_?NumericQ]:={x,0};
UpgradePairHorizontal[{x_?NumericQ,y_?NumericQ}]:={x,y};


UpgradePairVertical[None]:={0,0};
UpgradePairVertical[y_?NumericQ]:={0,y};
UpgradePairVertical[{x_?NumericQ,y_?NumericQ}]:={x,y};


FigCoordinatePointPattern={
((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)]),
((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)])
}|(Scaled|Canvas)[{_?NumericQ,_?NumericQ}];


FigPointPattern=(FigCoordinatePointPattern|ObjectPattern[FigAnchor]|ObjectNamePattern[FigAnchor]);


FigResolvePoint[p:{_?NumericQ,_?NumericQ}]:=(CurrentWindow[]@TFunction[])@p;
FigResolvePoint[Canvas[p:{_?NumericQ,_?NumericQ}]]:=p;
FigResolvePoint[Scaled[p:{_?NumericQ,_?NumericQ}]]:=(CurrentWindow[]@ScaledTFunction[])@p;
FigResolvePoint[a:ObjectPattern[FigAnchor]]:=a@GetPoint[];
FigResolvePoint[n:ObjectNamePattern[FigAnchor]]:=Object[n]@GetPoint[];


FigResolvePoint[
p:Except[
{_?NumericQ,_?NumericQ},
{
x:((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)]),
y:((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)])
}
]
]:={FigResolveCoordinate[x,1],FigResolveCoordinate[y,2]};


FigPointBoundingBox[p:{x_?NumericQ,y_?NumericQ}]:={{x,x},{y,y}};


FigDisplacementPattern=None|({_?NumericQ,_?NumericQ}|Scaled[{_?NumericQ,_?NumericQ}]|Canvas[{_?NumericQ,_?NumericQ}])|{
((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)]),
((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)])
};
FigDisplacementSequencePattern[n_Integer]:=Repeated[_?(MatchQ[#,FigDisplacementPattern]&),{n,Infinity}];
FigDisplacementSetPattern[n_Integer]:={Repeated[_?(MatchQ[#,FigDisplacementPattern]&),{n,Infinity}]};


FigResolveDisplacement[None]:={0,0};
FigResolveDisplacement[d:{_?NumericQ,_?NumericQ}]:=(CurrentWindow[]@DeltaTFunction[])@d;
FigResolveDisplacement[Canvas[d:{_?NumericQ,_?NumericQ}]]:=d;
FigResolveDisplacement[Scaled[d:{_?NumericQ,_?NumericQ}]]:=(CurrentWindow[]@ScaledDeltaTFunction[])@d;


FigResolveDisplacement[
p:Except[
{_?NumericQ,_?NumericQ},
{
x:((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)]),
y:((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)])
}
]
]:={FigResolveCoordinateDisplacement[x,1],FigResolveCoordinateDisplacement[y,2]};


RelativeTo[p:FigPointPattern,Args:FigDisplacementSequencePattern[0]]:=Module[
{Anchor,CanvasPoint},
FigCheckInFigure[RelativeTo];
Anchor=FigAnchor[p];
CanvasPoint=Anchor@GetPoint[]+Plus@@(FigResolveDisplacement/@{Args});
FigAnchor[Canvas[CanvasPoint],Anchor@GetOffset[],Anchor@GetAngle[]]
];


AlongAnchor[p:FigPointPattern,d_?NumericQ]/;(SciDraw`Private`$InFigure):=Module[
{Anchor,theta,CanvasPoint},
FigCheckInFigure[RelativeTo];
Anchor=FigAnchor[p];
theta=Anchor@GetAngle[];
CanvasPoint=Anchor@GetPoint[]+d*{Cos[theta],Sin[theta]};
FigAnchor[Canvas[CanvasPoint],Anchor@GetOffset[],Anchor@GetAngle[]]
];


NamedPointPattern=Center|Left|Right|Bottom|Top|TopLeft|TopRight|BottomLeft|BottomRight;


NamedPointOffset[Center]={0,0};
NamedPointOffset[Left]={-1,0};
NamedPointOffset[Right]={+1,0};
NamedPointOffset[Bottom]={0,-1};
NamedPointOffset[Top]={0,+1};


NamedPointOffset[TopLeft]={-1,+1};
NamedPointOffset[TopRight]={+1,+1};
NamedPointOffset[BottomLeft]={-1,-1};
NamedPointOffset[BottomRight]={+1,-1};


FigOffsetPattern=NamedPointPattern|NumericalPairPattern;


FigResolveOffset[Offset:NamedPointPattern]:=NamedPointOffset[Offset];
FigResolveOffset[Offset:NumericalPairPattern]:=Offset;


RangeParametersPattern=(None|(_?NumericQ)|{(_?NumericQ),(_?NumericQ)}|{{(_?NumericQ),(_?NumericQ)},{(_?NumericQ),(_?NumericQ)}});
NonNegativeRangeParametersPattern=(None|NonNegativePattern|{NonNegativePattern,NonNegativePattern}|{{NonNegativePattern,NonNegativePattern},{NonNegativePattern,NonNegativePattern}});
NumericalRegionPattern={{(_?NumericQ),(_?NumericQ)},{(_?NumericQ),(_?NumericQ)}};


UpgradeRangeParameters[xy_?NumericQ]:=UpgradeRangeParameters[{xy,xy}];
UpgradeRangeParameters[{x_?NumericQ,y_?NumericQ}]:=UpgradeRangeParameters[{{x,x},{y,y}}];
UpgradeRangeParameters[{{x1_?NumericQ,x2_?NumericQ},{y1_?NumericQ,y2_?NumericQ}}]:={{x1,x2},{y1,y2}};
UpgradeRangeParameters[None]={{0,0},{0,0}};


ExtendRegion[PRange:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},PDiff:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},Mode:Absolute]:=PRange+PDiff*{{-1,+1},{-1,+1}};
ExtendRegion[PRange:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},PFrac:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},Mode:Scaled]:=PRange+PFrac*{{-1,+1},{-1,+1}}*-Subtract@@@PRange;


FigRegionPattern=(All|{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}}|(Scaled|Canvas)[{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}}]);


FigResolveRegion[r:All]:=(CurrentWindow[]@CanvasRegion[]);
FigResolveRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}}]:=TransformRegion[CurrentWindow[]@TFunction[],r];
FigResolveRegion[Canvas[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}}]]:=r;
FigResolveRegion[Scaled[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}}]]:=TransformRegion[CurrentWindow[]@ScaledTFunction[],r];


FigExtendRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},d:None]:=r;
FigExtendRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},d:Automatic]:=ExtendRegion[r,UpgradeRangeParameters[0.02],Scaled];
FigExtendRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},d:RangeParametersPattern]:=ExtendRegion[r,TransformRegion[CurrentWindow[]@DeltaTFunction[],UpgradeRangeParameters[d]],Absolute];
FigExtendRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},Canvas[d:RangeParametersPattern]]:=ExtendRegion[r,UpgradeRangeParameters[d],Absolute];
FigExtendRegion[r:{{_?NumericQ,_?NumericQ},{_?NumericQ,_?NumericQ}},Scaled[d:RangeParametersPattern]]:=ExtendRegion[r,UpgradeRangeParameters[d],Scaled];


FigDeltaRegionPattern=Automatic|None|RangeParametersPattern|(Scaled|Canvas)[RangeParametersPattern];


Options[AdjustRegion]={RegionExtension->None,RegionDisplacement->None};
AdjustRegion[r:FigRegionPattern,Opts___?OptionQ]:=Module[
{
FullOptions=Flatten[{Opts,Options[AdjustRegion]}],CanvasRegion
},

(* validate options *)
FigCheckOption[AdjustRegion,RegionDisplacement,FigDisplacementPattern,FullOptions];
FigCheckOption[AdjustRegion,RegionExtension,FigDeltaRegionPattern,FullOptions];

(* resolve given region to canvas coordinates *)
CanvasRegion=FigResolveRegion[r];

(* do displacement *)
CanvasRegion=CanvasRegion+FigResolveDisplacement[(RegionDisplacement/.FullOptions)];

(* do extension *)
CanvasRegion=FigExtendRegion[CanvasRegion,(RegionExtension/.FullOptions)];

(* return *)
Canvas[CanvasRegion]

];
DeclareFigFallThroughError[AdjustRegion];


RegionPoint[r:FigRegionPattern,Offset:FigOffsetPattern]:=Module[
{UsedOffset,CanvasRegion,CanvasCenter,CanvasRadius},

(* resolve geometry *)
UsedOffset=FigResolveOffset[Offset];
CanvasRegion=FigResolveRegion[r];
CanvasCenter=Mean/@CanvasRegion;
CanvasRadius=-(Subtract@@@CanvasRegion)/2;

(* return offset point *)
Canvas[CanvasCenter+UsedOffset*CanvasRadius]
];


ExtractBoundingBox[Object[n:ObjectNamePattern[FigAnchor]]|(n:ObjectNamePattern[FigAnchor])]:=FigPointBoundingBox[Object[n]@GetPoint[]];
ExtractBoundingBox[Object[n:ObjectNamePattern[FigObject]]|(n:ObjectNamePattern[FigObject])]:=(Object[n]@MakeBoundingBox[]);


ObjectCanvasBox[ObjectList:{(ObjectPattern[FigAnchor]|ObjectNamePattern[FigAnchor]|ObjectPattern[FigObject]|ObjectNamePattern[FigObject])..}]:=Module[
{RegionList,ExtremumLists},
RegionList=(ExtractBoundingBox/@ObjectList);
ExtremumLists=MapThread[List,RegionList,2]; (* {{xminlist,xmaxlist},{yminlist,ymaxlist}} *)
MapAt[Max,MapAt[Min,ExtremumLists,{{1,1},{2,1}}],{{1,2},{2,2}}]
];


BoundingRegion[
ObjectList:{(ObjectPattern[FigAnchor]|ObjectNamePattern[FigAnchor]|ObjectPattern[FigObject]|ObjectNamePattern[FigObject])..}
]:=Module[
{},
FigCheckInFigure[BoundingRegion];
Canvas[ObjectCanvasBox[ObjectList]]
];
BoundingRegion[
obj:((ObjectPattern[FigAnchor]|ObjectNamePattern[FigAnchor]|ObjectPattern[FigObject]|ObjectNamePattern[FigObject]))
]:=BoundingRegion[{obj}];
DeclareFigFallThroughError[BoundingRegion];


EdgeMaskingFunction[DataEntry_,MaskEntry_,ExteriorEdgeMaskEntry_,Filler_]:=If[
(MaskEntry===True)||((MaskEntry===Exterior)&&ExteriorEdgeMaskEntry),
DataEntry,
Filler
];


MaskEdgeOption[
Data:{{_,_},{_,_}},
Mask:{{Exterior|LogicalPattern,Exterior|LogicalPattern},{Exterior|LogicalPattern,Exterior|LogicalPattern}},ExteriorEdgeMask:{{LogicalPattern,LogicalPattern},{LogicalPattern,LogicalPattern}},
Filler_
]:=MapThread[
EdgeMaskingFunction[##,Filler]&,
{Data,Mask,ExteriorEdgeMask},
2
];


ResolveAutomaticEdgeOption[
Data:{{_,_},{_,_}},
Defaults:{{_,_},{_,_}}
]:=MapThread[
Replace[#1,{Automatic->#2}]&,
{Data,Defaults},
2
];


RotateAnchor[p:FigPointPattern,theta:ScalarParameterPattern]/;(SciDraw`Private`$InFigure):=Module[
{Anchor,CanvasPoint},
FigCheckInFigure[RotateAnchor];
Anchor=FigAnchor[p];
FigAnchor[Canvas[Anchor@GetPoint[]],Anchor@GetOffset[],Anchor@GetAngle[]+UpgradeScalar[theta]]
];


AnchorAngle[p:FigPointPattern]:=Module[
{Anchor},
FigCheckInFigure[AnchorAngle];
Anchor=FigAnchor[p];
Anchor@GetAngle[]
];


AnchorOffset[p:FigPointPattern]:=Module[
{Anchor},
FigCheckInFigure[AnchorAngle];
Anchor=FigAnchor[p];
Anchor@GetOffset[]
];


AnchorCoordinates[p:FigPointPattern]:=Module[
{Anchor,CanvasCoordinates},
FigCheckInFigure[AnchorCoordinates];
Anchor=FigAnchor[p];
CanvasCoordinates=Anchor@GetPoint[];
(CurrentWindow[]@InverseTFunction[])@CanvasCoordinates
];


CanvasRayAngle[{p1:FigPointPattern,p2:FigPointPattern}]:=Module[
{CanvasCoordinates1,CanvasCoordinates2},
FigCheckInFigure[CanvasAngle];
VectorArcTan[FigResolvePoint[p2]-FigResolvePoint[p1]]
];


FigCoordinatePattern=((_?NumericQ)|(Scaled|Canvas)[(_?NumericQ)]|FigPointPattern);


AntiCoordinateIndex[1]=2;
AntiCoordinateIndex[2]=1;


FigResolveCoordinateIndex[Horizontal]=1;
FigResolveCoordinateIndex[Vertical]=2;


FigResolveCoordinate[x_?NumericQ,CoordinateIndex:(1|2)]:=FigResolvePoint[x*UnitVector[CoordinateIndex]][[CoordinateIndex]];
FigResolveCoordinate[Scaled[x_?NumericQ],CoordinateIndex:(1|2)]:=FigResolvePoint[Scaled[x*UnitVector[CoordinateIndex]]][[CoordinateIndex]];
FigResolveCoordinate[Canvas[x_?NumericQ],CoordinateIndex:(1|2)]:=x;
FigResolveCoordinate[p:FigPointPattern,CoordinateIndex:(1|2)]:=FigResolvePoint[p][[CoordinateIndex]];


FigResolveCoordinate[x_,Horizontal]:=FigResolveCoordinate[x,1];
FigResolveCoordinate[x_,Vertical]:=FigResolveCoordinate[x,2];


FigResolveCoordinateDisplacement[x_?NumericQ,CoordinateIndex:(1|2)]:=FigResolveDisplacement[x*UnitVector[CoordinateIndex]][[CoordinateIndex]];
FigResolveCoordinateDisplacement[Scaled[x_?NumericQ],CoordinateIndex:(1|2)]:=FigResolveDisplacement[Scaled[x*UnitVector[CoordinateIndex]]][[CoordinateIndex]];
FigResolveCoordinateDisplacement[Canvas[x_?NumericQ],CoordinateIndex:(1|2)]:=x;
FigResolveCoordinateDisplacement[p:FigPointPattern,CoordinateIndex:(1|2)]:=FigResolveDisplacement[p][[CoordinateIndex]];


FigResolveCoordinateDisplacement[x_,Horizontal]:=FigResolveCoordinateDisplacement[x,1];
FigResolveCoordinateDisplacement[x_,Vertical]:=FigResolveCoordinateDisplacement[x,2];


FigPointSetPattern[n_Integer]:={Repeated[FigPointPattern,{n,Infinity}]};


CentroidPoint[PointSet:FigPointSetPattern[1]]:=Canvas[Mean[FigResolvePoint/@PointSet]];


FigPointSetBoundingBox[PointSet:FigPointSetPattern[1]]:={{Min[First/@PointSet],Max[First/@PointSet]},{Min[Last/@PointSet],Max[Last/@PointSet]}};


FigRadiusPattern=NonNegativeIntervalParametersPattern|((Horizontal|Vertical|Canvas|Scaled)[NonNegativeIntervalParametersPattern])|{
((_?NonNegative)|(Scaled|Canvas)[(_?NonNegative)]),
((_?NonNegative)|(Scaled|Canvas)[(_?NonNegative)])
};


MakeRectangleGeometry[p:FigPointPattern,FullOptions_List]:=Module[
{Anchor,UsedAnchorOffset,UsedPivotOffset,
CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle},

(* resolve arguments *)
Anchor=FigAnchor[p];
CanvasRadius=FigResolveRadius[(Radius/.FullOptions)];
UsedAnchorOffset=FigResolveOffset[(AnchorOffset/.FullOptions)];
UsedPivotOffset=FigResolveOffset[ResolveOption[PivotOffset,{Automatic->UsedAnchorOffset},FullOptions]];
RotationAngle=UpgradeScalar[ResolveOption[Rotate,{Automatic->AnchorAngle[Anchor]},FullOptions]];

(* derived geometry *)
CanvasCenter=FigResolvePoint[Anchor]-UsedAnchorOffset*CanvasRadius;
CanvasPivot=CanvasCenter+UsedPivotOffset*CanvasRadius;

(* combined answer *)
{CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle}
];


MakeRectangleGeometry[p1:FigPointPattern,p2:FigPointPattern,FullOptions_List]:=Module[
{CanvasCorner1,CanvasCorner2,CanvasCenter,CanvasPivot,CanvasRadius,RotationAngle,UsedRoundingRadius},

(* generate geometry *)
CanvasCorner1=FigResolvePoint[p1];
CanvasCorner2=FigResolvePoint[p2];
CanvasCenter=(CanvasCorner2+CanvasCorner1)/2;
CanvasRadius=(CanvasCorner2-CanvasCorner1)/2;

(* invoke base form *)
MakeRectangleGeometry [
Canvas[CanvasCenter],
Join[{AnchorOffset->Center,Radius->Canvas[CanvasRadius]},FullOptions]
]

];


MakeRectangleGeometry[r:FigRegionPattern,FullOptions_List]:=Module[
{CanvasRegion,CanvasCorner1,CanvasCorner2,CanvasCenter,CanvasPivot,CanvasRadius,RotationAngle,UsedRoundingRadius},

(* generate geometry *)
CanvasRegion=FigResolveRegion[r];
{CanvasCorner1,CanvasCorner2}=Transpose@CanvasRegion;
CanvasCenter=(CanvasCorner2+CanvasCorner1)/2;
CanvasRadius=(CanvasCorner2-CanvasCorner1)/2;

(* invoke base form *)
MakeRectangleGeometry [
Canvas[CanvasCenter],
Join[{AnchorOffset->Center,Radius->Canvas[CanvasRadius]},FullOptions]
]
];


(*RectangleRegion[Args___]:=Canvas[FigRectangleBoundingBox@@MakeRectangleGeometry[Args]];*)


FigRectangleOptions={
(* geometry *)
Radius->1,AnchorOffset->Center,PivotOffset->Automatic,Rotate->None};


FigCheckRectangleOptions[Self_Object]:=Module[
{},
FigCheckOption[Self,Radius,FigRadiusPattern,FigOptions];
FigCheckOption[Self,AnchorOffset,FigOffsetPattern,FigOptions];
FigCheckOption[Self,PivotOffset,Automatic|FigOffsetPattern,FigOptions];
FigCheckOption[Self,Rotate,Automatic|ScalarParameterPattern,FigOptions];
];


FigResolveRadius[r:NonNegativeIntervalParametersPattern]:=(CurrentWindow[]@DeltaTFunction[])@UpgradePairEqual[r];


FigResolveRadius[Horizontal[r:NonNegativeIntervalParametersPattern]]:=Module[
{LengthUnit},
LengthUnit=First[(CurrentWindow[]@DeltaTFunction[])@{1,0}];
LengthUnit*UpgradePairEqual[r]
];


FigResolveRadius[Vertical[r:NonNegativeIntervalParametersPattern]]:=Module[
{LengthUnit},
LengthUnit=Last[(CurrentWindow[]@DeltaTFunction[])@{0,1}];
LengthUnit*UpgradePairEqual[r]
];


FigResolveRadius[Canvas[r:NonNegativeIntervalParametersPattern]]:=UpgradePairEqual[r];


FigResolveRadius[Scaled[r:NonNegativeIntervalParametersPattern]]:=(CurrentWindow[]@ScaledDeltaTFunction[])@UpgradePairEqual[r];


FigResolveRadius[
p:Except[
{_?NumericQ,_?NumericQ},
{
x:((_?NonNegative)|(Scaled|Canvas)[(_?NonNegative)]),
y:((_?NonNegative)|(Scaled|Canvas)[(_?NonNegative)])
}
]
]:=FigResolveDisplacement[p];


RectangleOffsetPoint[CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,Offset:{_?NumericQ,_?NumericQ}]:=RotationTransform[RotationAngle,CanvasPivot]@(CanvasCenter+CanvasRadius*Offset);


RectangleSideSegment[CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,Side:Left]:={
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{-1,-1}],
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{-1,+1}]
};
RectangleSideSegment[CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,Side:Right]:={
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{+1,-1}],
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{+1,+1}]
};
RectangleSideSegment[CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,Side:Bottom]:={
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{-1,-1}],
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{+1,-1}]
};
RectangleSideSegment[CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,Side:Top]:={
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{-1,+1}],
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,{+1,+1}]
};


FigRectangleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:NamedPointPattern,
Arg:None
]:=FigAnchor[Canvas[RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,NamedPointOffset[Name]]],-NamedPointOffset[Name],RotationAngle];


FigRectangleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:Offset,
OffsetValue:FigOffsetPattern
]:=FigAnchor[Canvas[RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,FigResolveOffset[OffsetValue]]],{0,0},RotationAngle];


FigRectangleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:(Left|Right|Bottom|Top),
x_?NumericQ
]:=FigAnchor[
Canvas[
InterpolateSegment[
RectangleSideSegment[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,Name],
Center,Scaled,x
]
],
-NamedPointOffset[Name],
RotationAngle
];


FigRectangleBoundingBox[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ
]:=FigPointSetBoundingBox[Table[
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,Offset],
{Offset,{{-1,-1},{-1,+1},{+1,-1},{+1,+1}}}
]
];


FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:(Center|Left|Right|Bottom|Top),
Arg:None
]:=FigAnchor[Canvas[RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,NamedPointOffset[Name]]],-NamedPointOffset[Name],RotationAngle];


FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:Offset,
OffsetValue:FigOffsetPattern
]:=FigAnchor[Canvas[RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,FigResolveOffset[OffsetValue]]],{0,0},RotationAngle];


FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
OrthoMode:(Normal|Tangent),
Arg:((t_?NumericQ)|{"Angle",t_?NumericQ})
]:=Module[
{theta,AnchorPoint,TangentAngle,AnchorAngle,AnchorOffset,ArcSense},

(* arc sense: +1 for CCW or -1 for CW *)
ArcSense=Sign[theta2-theta1];

theta=Switch[
Arg,
{"Angle",_?NumericQ},t, (* literal angle, before rotation *)
_?NumericQ,RescaleInterval[{0,1},{theta1,theta2}][t]  (* scaled from 0 to 1 on displayed arc *)
];
AnchorPoint=RotationTransform[RotationAngle,CanvasPivot]@(CanvasCenter+CanvasRadius*{Cos[theta],Sin[theta]});
AnchorOffset=Switch[OrthoMode,Normal,{1,0},Tangent,{0,1}]*ArcSense;
(* trig gives tangent vector in negative theta sense -- for arc tangent, this must be flipped if delta theta is positive; for outward normal, this must have Pi/2 added  *)
TangentAngle=VectorArcTan[CanvasRadius*{Sin[theta],-Cos[theta]}]+RotationAngle; 
(* trig gives tangent vector in negative theta sense, which y  *)
AnchorAngle=TangentAngle+Switch[OrthoMode,Normal,+Pi/2,Tangent,Switch[ArcSense,+1,Pi,-1|0,0]];

FigAnchor[Canvas[AnchorPoint],AnchorOffset,AnchorAngle]
];
FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
OrthoMode:(Normal|Tangent),
Arg:None]:=FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,
OrthoMode,0.5
];


FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:Tail,
Arg:None
]:=FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,Tangent,0];
FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:Head,
Arg:None
]:=FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,Tangent,1];


FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:(HeadRadius|TailRadius),
Arg:(p_?NumericQ)
]:=Module[
{CenterPoint,EndPoint},
CenterPoint=FigResolvePoint[FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,Center,None]];
EndPoint=FigResolvePoint[FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,Switch[Name,HeadRadius,Head,TailRadius,Tail],None]];
FigCurveAnchorFromPoints[{CenterPoint,EndPoint},Switch[Name,HeadRadius,Left,TailRadius,Right],p]
];
FigCircleAnchor[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ,
Name:(HeadRadius|TailRadius),
Arg:None]:=FigCircleAnchor[CanvasCenter,CanvasRadius,AngleRange,CanvasPivot,RotationAngle,
Name,0.5
];


FigCircleBoundingBox[
CanvasCenter:{_?NumericQ,_?NumericQ},CanvasRadius:{_?NumericQ,_?NumericQ},AngleRange:{theta1_?NumericQ,theta2_?NumericQ},CanvasPivot:{_?NumericQ,_?NumericQ},RotationAngle_?NumericQ
]:=FigPointSetBoundingBox[Table[
RectangleOffsetPoint[CanvasCenter,CanvasRadius,CanvasPivot,RotationAngle,Offset],
{Offset,{{0,-1},{0,+1},{-1,0},{+1,0}}}
]
];


FigCurvePointPattern=FigPointPattern|(FromTail|FromHead)[FigDisplacementSequencePattern[0]|(_?NumericQ)];
FigCurvePointSetPattern={Repeated[FigCurvePointPattern,{2,Infinity}]};
FigCurvePattern=FigCurvePointSetPattern|(_Graphics)|(_ContourGraphics);


FigCurveOptions={
(* curve geometry *)
TailRecess->None,HeadRecess->None,

(* curve extraction *)
Line->1

};


FigCheckCurveOptions[Self_Object]:=Module[
{},
FigCheckOption[Self,TailRecess,ScalarParameterPattern,FigOptions];
FigCheckOption[Self,HeadRecess,ScalarParameterPattern,FigOptions];
FigCheckOption[Self,Line,Join|All|((_Integer)?Positive),FigOptions];

];


General::figcurveref="Circular reference in curve specification, with tail point `1` and head point `2`.";


FigResolveCurve[Self_Object,Points:FigCurvePointSetPattern,FullOptions_List]:=Module[
{p1,p2,DereferencedPoints,CanvasPoints,CanvasHead,CanvasTail},

(* resolve FromTail and FromHead displacement references *)
p1=First[Points];
p2=Last[Points];
If[
MatchQ[p1,_FromTail]||MatchQ[p2,_FromHead]||(MatchQ[p1,_FromHead]&&MatchQ[p2,_FromTail]),
FigError[Self,"figcurveref",p1,p2]
];
DereferencedPoints=Replace[Points,{
FromTail[Args:FigDisplacementSequencePattern[0]]:>RelativeTo[p1,Args],
FromTail[d_?NumericQ]:>AlongAnchor[p1,d],
FromHead[Args:FigDisplacementSequencePattern[0]]:>RelativeTo[p2,Args],
FromHead[d_?NumericQ]:>AlongAnchor[p2,d]
},{1}];

(* resolve points *)
CanvasPoints=FigResolvePoint/@DereferencedPoints;

(* do tail and head recess *)
CanvasTail=InterpolateSegment[
Take[CanvasPoints,2],
Tail,Absolute,
UpgradeScalar[(TailRecess/.FullOptions)]
];
CanvasHead=InterpolateSegment[
Take[CanvasPoints,-2],
Head,Absolute,
-UpgradeScalar[(HeadRecess/.FullOptions)]
];
ReplacePart[CanvasPoints,{1->CanvasTail,-1->CanvasHead}]

];


FigResolveCurve[Self_Object,G:((_Graphics)|(_ContourGraphics)),FullOptions_List]:=Module[
{GrabbedPoints,CanvasPoints,CanvasTail,CanvasHead},

(* resolve points *)
GrabbedPoints=GrabPoints[G,FilterRules[FullOptions,Options[GrabPoints]]];
(* flatten a list of curves *)
If[
((Line/.FullOptions)==All)&&(Length[GrabbedPoints]!=0),
GrabbedPoints=Flatten[GrabbedPoints,1]
];
FigCheckValue[Self,GrabbedPoints,{Repeated[NumericalPairPattern,{2,Infinity}]},"list of extracted points"];
CanvasPoints=FigResolvePoint/@GrabbedPoints;

(* do tail and head recess *)
CanvasTail=InterpolateSegment[
Take[CanvasPoints,2],
Tail,Absolute,
UpgradeScalar[(TailRecess/.FullOptions)]
];
CanvasHead=InterpolateSegment[
Take[CanvasPoints,-2],
Head,Absolute,
-UpgradeScalar[(HeadRecess/.FullOptions)]
];
ReplacePart[CanvasPoints,{1->CanvasTail,-1->CanvasHead}]

];


LinearInterpolationFunctionsRxRy[CanvasPoints_List]:=Module[
{
Segments,SegmentLengths,TotalLength,CurveParameterValues,
XFunction,YFunction,DXFunction,DYFunction
},
Segments=Partition[CanvasPoints,2,1];
SegmentLengths=SegmentLength/@Segments;
CurveParameterValues=Prepend[Accumulate[SegmentLengths],0];
TotalLength=Last[CurveParameterValues];
CurveParameterValues=CurveParameterValues/TotalLength;

(* set up curve interpolation *)
(* functions for x[d], y[d], theta[d] *)

XFunction=Interpolation[Transpose[{CurveParameterValues,First/@CanvasPoints}],InterpolationOrder->1];
YFunction=Interpolation[Transpose[{CurveParameterValues,Last/@CanvasPoints}],InterpolationOrder->1];

{XFunction,YFunction}

];


LinearInterpolationFunctionPair[CanvasPoints_List]:=Module[
{
XFunction,YFunction,DXFunction,DYFunction
},
{XFunction,YFunction}=LinearInterpolationFunctionsRxRy[CanvasPoints];
{DXFunction,DYFunction}=Derivative[1]/@{XFunction,YFunction};

(* construct functions *)
(* note: use of With is necessary to force correct functions into pure function definitions, rather than just leaving references to the local symbols XFunction, etc. *)
With[
{x=XFunction,y=YFunction,dx=DXFunction,dy=DYFunction},
{{x[#],y[#]}&,{dx[#],dy[#]}&}
]

];


CurveSegment::badsegment="A curve with `1` points does not have a segment numbered `2`.";
CurveSegment::badsegmentzero="Zero is not a valid segment number.";


CurveSegment[Points_List,s_Integer]:=Module[
{n=Length[Points]},
Which[
s>n-1,FigError[CurveSegment,"badsegment",n,s],
s<-(n-1),FigError[CurveSegment,"badsegment",n,s],
s==0,FigError[CurveSegment,"badsegmentzero"],
s>0,Points[[s;;s+1]],
s<0,Points[[s-1;;s]]
]
];


CurvePoint::badpoint="A curve with `1` points does not have a point numbered `2`.";
CurvePoint::badpointzero="Zero is not a valid segment number.";


CurvePoint[Points_List,k_Integer]:=Module[
{n=Length[Points]},
Which[
k>n,FigError[CurvePoint,"badsegment",n,k],
k<-n,FigError[CurvePoint,"badsegment",n,k],
k==0,FigError[CurvePoint,"badsegmentzero"],
k>0,Points[[k]],
k<0,Points[[k]]
]
];


FigCurveAnchorFromPoints[Points_List,Name_,Arg_]:=FigCurveAnchor[Points,LinearInterpolationFunctionPair[Points],Name,Arg];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Name_,{s_Integer,Arg_}]:=FigCurveAnchorFromPoints[
CurveSegment[Points,s],
Name,
Arg
];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Side:(Left|Center|Right),None]:=FigCurveAnchor[Points,InterpolationFunctionPair,Side,0.5];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Side:(Left|Center|Right),u:(_?NumericQ)]:=FigAnchor[
Canvas[InterpolationFunction[u]],
Switch[Side,Right,{0,+1},Center,{0,0},Left,{0,-1}],
VectorArcTan[TangentFunction[u]] 
];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Side:(Left|Center|Right),{FromTail,d_?NumericQ}]:=AlongAnchor[
FigCurveAnchor[Points,InterpolationFunctionPair,Center,0],
d
];
FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Side:(Left|Center|Right),{FromHead,d_?NumericQ}]:=AlongAnchor[
FigCurveAnchor[Points,InterpolationFunctionPair,Center,1],
d
];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Side:(Left|Center|Right),{CoordinateIndexName:(Horizontal|Vertical),x:FigCoordinatePattern}
]:=Module[
{fRc,xc,uRule,u,CoordinateIndex},
fRc=LinearInterpolationFunctionsRxRy[Points];  (* AD HOC -- perhaps to be revised if these become the stored interpolation functions *)
CoordinateIndex=FigResolveCoordinateIndex[CoordinateIndexName];
xc=FigResolveCoordinate[x,CoordinateIndex];

(* recover curve parameter *)
uRule=FindRoot[fRc[[CoordinateIndex]][u]-xc,{u,0,1}];
u=Last@Last@uRule;
FigAnchor[
Canvas[InterpolationFunction[u]],
Switch[Side,Right,{0,+1},Center,{0,0},Left,{0,-1}],
VectorArcTan[TangentFunction[u]] 
]
];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Tail,None]:=FigAnchor[FigCurveAnchor[Points,InterpolationFunctionPair,Center,0],Right];
FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Head,None]:=FigAnchor[FigCurveAnchor[Points,InterpolationFunctionPair,Center,1],Left];


FigCurveAnchor[Points_List,InterpolationFunctionPair:{InterpolationFunction_,TangentFunction_},Point,k_Integer]:=FigAnchor[Canvas[CurvePoint[Points,k]]];


FigCurveBoundingBox[CanvasCurve:{{_?NumericQ,_?NumericQ}...}]/;(Length[CanvasCurve]<1):=None;
FigCurveBoundingBox[CanvasCurve:{{_?NumericQ,_?NumericQ}...}]/;(Length[CanvasCurve]>=1):={
{Min[First/@CanvasCurve],Max[First/@CanvasCurve]},
{Min[Last/@CanvasCurve],Max[Last/@CanvasCurve]}
};


FigArrowheadOptions[st:LogicalPattern,sh:LogicalPattern,len_:6,lip_:3]:={
TailLength->len,TailLip->lip,ShowTail->st,
HeadLength->len,HeadLip->lip,ShowHead->sh
};
FigArrowheadOptions[show:LogicalPattern,len_,lip_,End]:={
TailLength->Automatic,TailLip->Automatic,ShowTail->Automatic,
HeadLength->Automatic,HeadLip->Automatic,ShowHead->Automatic,
EndLength->len,EndLip->lip,ShowEnd->show
};


FigCheckArrowheadOptions[Self_Object]:=Module[
{},

FigCheckOption[Self,TailLength,_?NumericQ,FigOptions];
FigCheckOption[Self,HeadLength,_?NumericQ,FigOptions];

FigCheckOption[Self,TailLip,NonNegativePattern|{NonNegativePattern,NonNegativePattern},FigOptions];
FigCheckOption[Self,HeadLip,NonNegativePattern|{NonNegativePattern,NonNegativePattern},FigOptions];

FigCheckOption[Self,ShowTail,LogicalPattern,FigOptions];
FigCheckOption[Self,ShowHead,LogicalPattern,FigOptions];
];


FigCheckArrowheadOptions[Self_Object,End]:=Module[
{},

FigCheckOption[Self,EndLength,_?NumericQ,FigOptions];
FigCheckOption[Self,TailLength,Automatic|(_?NumericQ),FigOptions];
FigCheckOption[Self,HeadLength,Automatic|(_?NumericQ),FigOptions];

FigCheckOption[Self,EndLip,NonNegativePattern|{NonNegativePattern,NonNegativePattern},FigOptions];
FigCheckOption[Self,TailLip,Automatic|NonNegativePattern|{NonNegativePattern,NonNegativePattern},FigOptions];
FigCheckOption[Self,HeadLip,Automatic|NonNegativePattern|{NonNegativePattern,NonNegativePattern},FigOptions];

FigCheckOption[Self,ShowEnd,LogicalPattern,FigOptions];
FigCheckOption[Self,ShowTail,Automatic|LogicalPattern,FigOptions];
FigCheckOption[Self,ShowHead,Automatic|LogicalPattern,FigOptions];

];


(*BasicArrowheadPoints::usage="BasicArrowheadPoints[anchor,headlength,{headlipl,headlipr},sense] generates the points for an arrowhead with location and orientation given by anchor.";*)


BasicArrowheadPoints[a:ObjectPattern[FigAnchor],HeadLength_?NumericQ,{HeadLipL_?NumericQ,HeadLipR_?NumericQ},Sense:(-1|1)]:=Module[
{P,theta},

P=(a@GetPoint[]);
theta=(a@GetAngle[]);
{
P+RotationTransform[N@theta][{HeadLength,HeadLipL}*{-Sense,+1}*If[HeadLipL>0,1,0]],
P,
P+RotationTransform[N@theta][{HeadLength,HeadLipR}*{-Sense,-1}*If[HeadLipR>0,1,0]]
}
];


FigCurveArrowheadPoints[TailAnchor:ObjectPattern[FigAnchor],HeadAnchor:ObjectPattern[FigAnchor],FullOptions_List]:=Module[
{},

{

(* tail *)
If[
(ShowTail/.FullOptions),
BasicArrowheadPoints[TailAnchor,(TailLength/.FullOptions),UpgradePair[(TailLip/.FullOptions)],-1],
{}
],

(* head *)
If[
(ShowHead/.FullOptions),
BasicArrowheadPoints[HeadAnchor,(HeadLength/.FullOptions),UpgradePair[(HeadLip/.FullOptions)],+1],
{}
]

}

];


FigCurveArrowheadPoints[TailAnchor:ObjectPattern[FigAnchor],HeadAnchor:ObjectPattern[FigAnchor],ReversedSide:LogicalPattern,FullOptions_List,End]:=Module[
{UsedTailLength,UsedHeadLength,UsedTailLip,UsedHeadLip,UsedShowTail,UsedShowHead},

{

(* tail *)
UsedShowTail=ResolveOption[ShowTail,{Automatic:>(ShowEnd/.FullOptions)},FullOptions];
If[
UsedShowTail,
UsedTailLength=ResolveOption[TailLength,{Automatic:>-(EndLength/.FullOptions)},FullOptions];
UsedTailLip=If[ReversedSide,Reverse,Identity]@UpgradePair[ResolveOption[TailLip,{Automatic:>(EndLip/.FullOptions)},FullOptions]];
BasicArrowheadPoints[TailAnchor,UsedTailLength,UsedTailLip,-1],
{}
],

(* head *)
UsedShowHead=ResolveOption[ShowHead,{Automatic:>(ShowEnd/.FullOptions)},FullOptions];
If[
UsedShowHead,
UsedHeadLength=ResolveOption[HeadLength,{Automatic:>-(EndLength/.FullOptions)},FullOptions];
UsedHeadLip=If[ReversedSide,Reverse,Identity]@UpgradePair[ResolveOption[HeadLip,{Automatic:>(EndLip/.FullOptions)},FullOptions]];
BasicArrowheadPoints[HeadAnchor,UsedHeadLength,UsedHeadLip,+1],
{}
]

}

];


GrabPoints::nocurve="No curves found.";
GrabPoints::fewcurve="Requested Line->`1` is greater than the number `2` of curves found.";
GrabPoints::shortcurve="Curve obtained `1` has fewer than two points and therefore may be unsuitable for use as an argument to functions or objects.";


Point2D3DPattern={_?NumericQ,_?NumericQ}|{_?NumericQ,_?NumericQ,_?NumericQ};
Options[GrabPoints]={Line->1};
GrabPoints[G:(_Graphics|_Graphics3D),Opts___?OptionQ]:=Module[
{FullOptions=Flatten[{Opts,Options[GrabPoints]}],LineSet,CurveSet,Curve},

(*validate options*)
FigCheckOption[GrabPoints,Line,Join|All|((_Integer)?Positive),FullOptions];

(*grab all curves*)
(* use Normal to resolve GraphicsComplex into Line (e.g., in output of ContourPlot) *)
LineSet=Cases[Normal[G],Line[{Point2D3DPattern..}]|Line[{{Point2D3DPattern..}..}],Infinity];
CurveSet=Replace[LineSet,{Line[c:{Point2D3DPattern..}]:>{c},Line[cc:{{Point2D3DPattern..}..}]:>cc},{1}];
CurveSet=Flatten[CurveSet,1];
If[Length[CurveSet]==0,FigMessage[GrabPoints,"nocurve"];
Return[{}]];

(*process resulting curve set*)
Switch[
(Line/.FullOptions),
(*case:grab specific curve*)
_Integer,
If[
Length[CurveSet]<(Line/.FullOptions),
FigMessage[GrabPoints,"fewcurve",(Line/.FullOptions),Length[CurveSet]];
Return[{}]
];
Curve=CurveSet[[(Line/.FullOptions)]];
If[Length[Curve]<2,FigMessage[GrabPoints,"shortcurve",Curve]];
Curve,
(*case:merge all curves*)
Join,
Curve=Join@@CurveSet;
If[Length[Curve]<2,FigMessage[GrabPoints,"shortcurve",Curve]];
Curve,
(*case:return curve list*)
All,
Do[
If[Length[Curve]<2,FigMessage[GrabPoints,"shortcurve",Curve]],
{Curve,CurveSet}
];
CurveSet
]
];
GrabPoints[CG:(_ContourGraphics),Opts___?OptionQ]:=GrabPoints[Graphics[CG],Opts];


SplitByDelimiter[l_List,d_Symbol]:=Module[
{SplitList},
SplitList=Split[l,!MemberQ[{#1,#2},d]&];
Cases[SplitList,Except[{d}]]
];
PurgeOfDelimiter[l_List,d_Symbol]:=Module[
{},
Cases[l,Except[d]]
];


AppendFirst[l_List]:=Append[l,First[l]];


End[];


Protect[Evaluate[$Context<>"*"]];
Unprotect[Evaluate[$Context<>"$*"]];
EndPackage[];
