Last active
August 29, 2015 14:06
-
-
Save dgwaldo/066e448c6e5447421ca4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class WellSurveyPlot3DViewModel : BindableBase | |
{ | |
private readonly List<WellXyzPoint> _calculatedSurveyPoints; | |
private int _xDir; | |
private int _yDir; | |
public WellSurveyPlot3DViewModel(List<WellXyzPoint> calculatedSurveyPoints) | |
{ | |
_calculatedSurveyPoints = calculatedSurveyPoints; | |
MajorGridSpacing = 300; //Meters | |
MinorGridSpacing = 150; //Meters | |
MarkerLabels = new List<BillboardTextItem>(); | |
Well = new Tube3DGeometryViewModel(); | |
} | |
/// <summary> Constructor for Blend. </summary> | |
public WellSurveyPlot3DViewModel() { } | |
public int MajorGridSpacing { get; set; } | |
public int MinorGridSpacing { get; set; } | |
public int GridLength { get; set; } | |
public int GridLineWidth { get; set; } | |
public int Xdir { get { return _xDir; } } | |
public int Ydir { get { return _yDir; } } | |
public Point3D BottomPlaneCenter { get; private set; } | |
public Point3D BackLeftPlaneCenter { get; private set; } | |
public Point3D BackRightPlaneCenter { get; private set; } | |
public List<BillboardTextItem> GridLabels { get; private set; } | |
public List<BillboardTextItem> MarkerLabels { get; private set; } | |
public PerspectiveCamera PerspectiveCamera { get; private set; } | |
public Tube3DGeometryViewModel Well { get; set; } | |
public void CreateWell3DPlot() | |
{ | |
Well.Path = CalculateGlobalCoordinates(_calculatedSurveyPoints.Last().MeasuredDepth); | |
SetGridSpacingAccordingToUnits(); | |
SetGridLength(); | |
SetSceneSizesAccordingToGridSize(); | |
SetXyAxisDirections(Well.Path.Last().X, Well.Path.Last().Y); | |
CalculateBackPlaneCenterPoints(); | |
SetupPerspectiveCamera(); | |
CreateAxisLables(); | |
OnPropertyChanged(""); | |
} | |
/// <summary> | |
/// Well must be plotted before calling this method. This will add a marker at the nearest 3D point in the well-bore. | |
/// </summary> | |
/// <param name="depth"></param> | |
/// <param name="text"></param> | |
public void AddWellBoreMarker(double depth, string text) | |
{ | |
if (Well.Path == null) throw new InvalidOperationException("Well path must contain points before a marker can be added."); | |
var idxPntAlongPath = _calculatedSurveyPoints.FindIndex(pnt => pnt.MeasuredDepth > depth); | |
MarkerLabels.Add(new BillboardTextItem { Text = text, Position = Well.Path[idxPntAlongPath] }); | |
OnPropertyChanged("MarkerLabels"); | |
} | |
/// <summary> | |
/// Creates a set of 3D points for the given global coordinates. | |
/// </summary> | |
/// <param name="depth">Depth given in users current units. </param> | |
/// <returns></returns> | |
protected Point3DCollection CalculateGlobalCoordinates(double depth) | |
{ | |
return new Point3DCollection(_calculatedSurveyPoints | |
.Where(x => x.MeasuredDepth <= depth) | |
.Select(pnt => new Point3D | |
{ | |
X = UnitsContext.Create.LengthLongUnit(-1 * pnt.Y).UserValue, | |
Y = UnitsContext.Create.LengthLongUnit(-1 * pnt.Z).UserValue, | |
Z = UnitsContext.Create.LengthLongUnit(-pnt.X).UserValue | |
})); | |
} | |
private void SetGridSpacingAccordingToUnits() | |
{ | |
if (UnitsContext.Current.LengthLong == LengthLongSymbols.Ft) | |
{ | |
MajorGridSpacing = 1000; | |
MinorGridSpacing = 500; | |
} | |
} | |
private void SetGridLength() | |
{ | |
var maxX = Well.Path.Min(x => x.X); | |
var maxY = Well.Path.Min(x => x.Y); | |
var minZ = Well.Path.Min(x => x.Z); | |
var maxAxis = new[] { Math.Abs(maxX), Math.Abs(maxY), Math.Abs(minZ) }.Max(); | |
var maxAxisToNearestMajorInterval = (int)RoundToNearest(maxAxis, MajorGridSpacing); | |
if (maxAxisToNearestMajorInterval % 2 != 0) | |
maxAxisToNearestMajorInterval += MajorGridSpacing; //Ensure odd number so we have true half point | |
GridLength = maxAxisToNearestMajorInterval; | |
} | |
/// <summary> Keeps the tube and grid proportionate no matter the zoom. </summary> | |
private void SetSceneSizesAccordingToGridSize() | |
{ | |
const double gridScaleFactor = .001; | |
const double tubeScaleFactor = .01; | |
Well.Diameter = (int)(GridLength * tubeScaleFactor); | |
GridLineWidth = (int)(GridLength * gridScaleFactor); | |
} | |
private void CalculateBackPlaneCenterPoints() | |
{ | |
// Logic for grid coordinates is odd, not the typical x,y directions | |
// +x-y | -x-y | |
// | | |
// ------ ------ | |
// +x+y | -x+y | |
// | | |
//If xDir and yDir and unmodified, Backplanes for 0 to 90 | |
var centerPoint = GridLength / 2; | |
BackLeftPlaneCenter = new Point3D(0, _yDir * centerPoint, -centerPoint); | |
BackRightPlaneCenter = new Point3D(_xDir * centerPoint, 0, -centerPoint); | |
BottomPlaneCenter = new Point3D(_xDir * centerPoint, _yDir * centerPoint, -GridLength); | |
} | |
private void SetupPerspectiveCamera() | |
{ | |
PerspectiveCamera = new PerspectiveCamera | |
{ | |
//Position: will be set when ZoomExtentsWhenLoaded is set to True in Xaml. | |
//Position = new Point3D(_xDir * GridLength, _yDir * GridLength, .5 * -GridLength), | |
LookDirection = new Vector3D(-1 * _xDir * GridLength, -1 * _yDir * GridLength, .5 * -GridLength), | |
UpDirection = new Vector3D(0, 0, 1), | |
FieldOfView = 20.0, | |
}; | |
} | |
private void SetXyAxisDirections(double lastX, double lastY) | |
{ | |
_xDir = -1; | |
_yDir = -1; | |
if (lastX < 0 & lastY > 0) //90 to 180 | |
_yDir = 1; | |
if (lastX > 0 & lastY > 0) //180 to 270 | |
{ _yDir = 1; _xDir = 1; } | |
if (lastX > 0 & lastY < 0) //270 to 0 | |
_xDir = 1; | |
} | |
private void CreateAxisLables() | |
{ | |
GridLabels = new List<BillboardTextItem>(); | |
for (var i = 0; i <= GridLength; i += MajorGridSpacing * 4) //Reduce number of labels by stepping | |
{ | |
GridLabels.Add(new BillboardTextItem | |
{ | |
Text = String.Format("{0} {1}", i.ToString("N0"), UnitsContext.CurrentSymbols.LengthLong), | |
Position = new Point3D(_xDir * i, _yDir * GridLength + 100, -GridLength), | |
WorldDepthOffset = 100 | |
}); | |
GridLabels.Add(new BillboardTextItem | |
{ | |
Text = String.Format("{0} {1}", i.ToString("N0"), UnitsContext.CurrentSymbols.LengthLong), | |
Position = new Point3D(_xDir * GridLength + 100, 0, -i), | |
WorldDepthOffset = 100 | |
}); | |
} | |
} | |
protected static double RoundToNearest(double amount, double roundTo) | |
{ | |
double remainder = amount % roundTo; | |
if (remainder < (roundTo / 2)) | |
{ | |
amount -= remainder; | |
} | |
else | |
{ | |
amount += (roundTo - remainder); | |
} | |
return amount; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Tube3DGeometryViewModel | |
{ | |
/// <summary> Tube diameter in coordinates relative to XYZ path. </summary> | |
public double Diameter { get; set; } | |
/// <summary> Number of sections around the circle. </summary> | |
public double ThetaDiv { get; set; } | |
/// <summary> XYZ Point collection. </summary> | |
public Point3DCollection Path { get; set; } | |
/// <summary> Brush used for fill, if set will also be used for the material. </summary> | |
public Brush Fill { get; set; } | |
/// <summary> Texture coordinates, define the value to show from a color gradient, at each XYZ point, normalized between 0 and 1. </summary> | |
public DoubleCollection TextureCoordinates { get; set; } | |
/// <summary> Material, uses the fill brush to create a material, can be used in place of fill. </summary> | |
public Material Material | |
{ | |
get { return MaterialHelper.CreateMaterial(Fill); } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment