Last active
August 29, 2015 14:10
-
-
Save dgwaldo/b83f1bdfcb0c8b3cb42c to your computer and use it in GitHub Desktop.
WellSurveyPlot3DValuesViewModel extends the WellSurveyPlot3DViewModel
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
/// <summary> | |
/// Extends the WellSurveyPlot3DViewModel to provide methods and properties for | |
/// viewing values along the path of a pipe, internal to the well-bore. | |
/// </summary> | |
public class WellSurveyPlot3DValuesViewModel : WellSurveyPlot3DViewModel | |
{ | |
private double _maxValueOnPipe; | |
private double _minValueOnPipe; | |
public WellSurveyPlot3DValuesViewModel(List<WellXyzPoint> globalXyz) | |
: base(globalXyz) | |
{ | |
Pipe = new Tube3DGeometryViewModel(); | |
PipeLegend = new RangeColorAxisViewModel(); | |
Well.Fill = new SolidColorBrush(Colors.LightGray) { Opacity = .1 }; | |
Pipe.Fill = Brushes.DarkGray; | |
} | |
/// <summary> Parameterless constructor for Blend. </summary> | |
public WellSurveyPlot3DValuesViewModel() { } | |
public Tube3DGeometryViewModel Pipe { get; set; } | |
public RangeColorAxisViewModel PipeLegend { get; set; } | |
/// <summary> | |
/// Creates a pipe that follows the well path, and paints the chosen color gradient as the pipes material. | |
/// </summary> | |
/// <param name="runDepth"> Run depth in (in). </param> | |
/// <param name="valuesAtDepthAlongPipe"> Values are normalized and used as texture coordinates. </param> | |
/// <param name="fill"> A linear gradient brush to apply to the pipe. </param> | |
public void CreatePipeIn3DWell(double runDepth, Dictionary<double, double> valuesAtDepthAlongPipe, LinearGradientBrush fill) | |
{ | |
if (Well.Path == null || Well.Path.Count < 1) throw new InvalidOperationException("Well must not be null and must have points."); | |
Pipe.Path = CalculateGlobalCoordinates(runDepth); | |
fill.StartPoint = new Point(0, 0); //Ensure the fill is horizontal for pipe. | |
fill.EndPoint = new Point(1, 0); | |
fill.Opacity = 1; | |
Pipe.Fill = fill; | |
Pipe.TextureCoordinates = NormalizeValues(InterpolatedValues(runDepth, valuesAtDepthAlongPipe)); | |
Pipe.Diameter = Well.Diameter - 10; | |
OnPropertyChanged("Pipe"); | |
} | |
/// <summary> Sets up the legend for showing the values along the pipe. | |
/// Sets the value step to be 10 equal intervals between the max and min TextureCoordinate values. </summary> | |
/// <param name="legendTitle">Title to display above legend. </param> | |
/// <param name="legenedBrush">Brush direction for legend should be vertical. </param> | |
/// <param name="format"></param> | |
public void SetupPipeLegend(string legendTitle, LinearGradientBrush legenedBrush, string format = "") | |
{ | |
if (Pipe.TextureCoordinates == null || Pipe.TextureCoordinates.Count < 1) throw new InvalidOperationException("Pipe texture coordinates must not be null and must have points."); | |
PipeLegend.LegendTitle = legendTitle; | |
PipeLegend.ColorScheme = legenedBrush; | |
PipeLegend.Step = (_maxValueOnPipe - _minValueOnPipe) / 10; | |
PipeLegend.Min = _minValueOnPipe; | |
PipeLegend.Max = _maxValueOnPipe; | |
PipeLegend.MinTextureCoordinate = Pipe.TextureCoordinates.Min(); | |
PipeLegend.MaxTextureCoordinate = Pipe.TextureCoordinates.Max(); | |
PipeLegend.AxisValueFormatString = format; | |
OnPropertyChanged("PipeLegend"); | |
} | |
/// <summary> | |
/// Interpolates the incoming values at each 3D point along the pipe path. | |
/// </summary> | |
/// <param name="runDepth"></param> | |
/// <param name="valuesAtDepthAlongPipe"></param> | |
/// <returns></returns> | |
private List<double> InterpolatedValues(double runDepth, Dictionary<double, double> valuesAtDepthAlongPipe) | |
{ | |
var interpolatedValues = new List<double>(); | |
var keys = new List<double>(valuesAtDepthAlongPipe.Keys); | |
var step = runDepth / Pipe.Path.Count - 1; | |
for (var i = 0; i < Pipe.Path.Count; i++) | |
{ | |
var depthIndx = Math.Abs(keys.BinarySearch(i * step)); | |
if (depthIndx >= keys.Count - 1) depthIndx -= 1; | |
var nearestDepth = keys[depthIndx]; | |
var nearestPrvDepth = (i > 0) ? keys[depthIndx - 1] : keys[depthIndx]; | |
var depth = (nearestDepth + nearestPrvDepth) / 2; | |
var val = valuesAtDepthAlongPipe[nearestDepth]; | |
var preVal = valuesAtDepthAlongPipe[nearestPrvDepth]; | |
interpolatedValues.Add(Interpolate(preVal, val, nearestPrvDepth, depth, nearestDepth)); | |
} | |
return interpolatedValues; | |
} | |
/// <summary> | |
/// Interpolates or extrapolates to find the unknown X. | |
/// X1,Y1 | |
/// Solve for X2 Y2 | |
/// X3,Y3 | |
/// </summary> | |
/// <param name="x1"></param> | |
/// <param name="y1"></param> | |
/// <param name="y2">Y-Value for which the function will return X.</param> | |
/// <param name="x3"></param> | |
/// <param name="y3"></param> | |
/// <remarks>http://www.ajdesigner.com/phpinterpolation/linear_interpolation_equation.php</remarks> | |
private double Interpolate(double x1, double x3, double y1, double y2, double y3) | |
{ | |
if (Math.Abs(y3 - y1 - 0) < .0001) return x1; | |
return x1 + (y2 - y1) * (x3 - x1) / (y3 - y1); | |
} | |
/// <summary> | |
/// Normalize values between 0 & 1 to match scaling of linear gradient color brush. | |
/// </summary> | |
/// <param name="valuesAtDepthAlongPipe"></param> | |
/// <returns></returns> | |
private DoubleCollection NormalizeValues(List<double> valuesAtDepthAlongPipe) | |
{ | |
_maxValueOnPipe = valuesAtDepthAlongPipe.Max(); | |
_minValueOnPipe = valuesAtDepthAlongPipe.Min(); | |
return new DoubleCollection(valuesAtDepthAlongPipe.Select(d => (d - _minValueOnPipe) / (_maxValueOnPipe - _minValueOnPipe))); | |
} | |
} |
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
<Grid> | |
<Grid.RowDefinitions> | |
<RowDefinition Height=".2*" /> | |
<RowDefinition Height="*"/> | |
<RowDefinition Height=".7*"/> | |
</Grid.RowDefinitions> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition Width=".2*"/> | |
<ColumnDefinition Width="*" /> | |
<ColumnDefinition Width=".2*"/> | |
</Grid.ColumnDefinitions> | |
<Grid Grid.Row="1" ZIndex="100" DockPanel.Dock="Left"> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="Auto" /> | |
<RowDefinition Height="*" /> | |
</Grid.RowDefinitions> | |
<TextBlock Grid.Row="0" Text="{Binding PipeLegend.LegendTitle}" /> | |
<h:RangeColorAxis Grid.Row="1" | |
FormatString="{Binding PipeLegend.AxisValueFormatString}" | |
Minimum="{Binding PipeLegend.Min}" | |
Maximum="{Binding PipeLegend.Max}" | |
MaximumTextureCoordinate="{Binding PipeLegend.MaxTextureCoordinate}" | |
MinimumTextureCoordinate="{Binding PipeLegend.MinTextureCoordinate}" | |
Step="{Binding PipeLegend.Step}" Margin="6" | |
Position="Left" | |
ColorScheme="{Binding PipeLegend.ColorScheme}" | |
FlipColorScheme="{Binding PipeLegend.FlipColorScheme}" /> | |
</Grid> | |
<DockPanel Grid.Row="0" Grid.RowSpan="3" Grid.ColumnSpan="3" Grid.Column="0" Background="White"> | |
<Menu DockPanel.Dock="Top"> | |
<!-Omitted for brevity--> | |
</Menu> | |
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Bottom" Opacity="0.5" > | |
<StackPanel> | |
<TextBlock Text="Well Diameter" /> | |
<Slider x:Name="WellDiamSlider" Value="{Binding Well.Diameter}" ToolTip="{Binding ElementName=WellDiamSlider, Path=Value}" Minimum="10" Maximum="500" Width="100" Margin="10"/> | |
</StackPanel> | |
<StackPanel> | |
<TextBlock Text="Pipe Diameter" /> | |
<Slider x:Name="PipeDiamSlider" Value="{Binding Pipe.Diameter}" ToolTip="{Binding ElementName=PipeDiamSlider, Path=Value}" Minimum="10" Maximum="500" Width="100" Margin="10"/> | |
</StackPanel> | |
<StackPanel> | |
<TextBlock Text="Grid-Line Thickness" /> | |
<Slider x:Name="GridSlider" Value="{Binding GridLineWidth}" | |
ToolTip="{Binding ElementName=GridSlider, Path=Value}" Minimum="1" Maximum="50" Width="100" Margin="10"/> | |
</StackPanel> | |
<StackPanel> | |
<CheckBox x:Name="PipeIsVisibleCheck" IsChecked="True" Content="Render Pipe" /> | |
<CheckBox x:Name="WellIsVisibleCheck" IsChecked="True" Content="Render Well" /> | |
<CheckBox x:Name="RenderGridCheckBox" IsChecked="True" Content="Render Grid " /> | |
<CheckBox x:Name="FreezeAxisLblsCheckBox" IsChecked="True" Content="Freeze Axis Labels" /> | |
</StackPanel> | |
</StackPanel> | |
<h:HelixViewport3D x:Name="HelixViewport3D" ZoomExtentsWhenLoaded="True" ShowCoordinateSystem="True" | |
CameraRotationMode="{Binding ElementName=RotationMode, Path=SelectedItem}" | |
DefaultCamera="{Binding PerspectiveCamera, UpdateSourceTrigger=PropertyChanged}" | |
CameraMode="{Binding ElementName=CameraMode, Path= SelectedItem}" | |
PanGesture="LeftClick" > | |
<h:DefaultLights /> | |
<h:TubeVisual3D x:Name="PipeVisual" Path="{Binding Pipe.Path}" | |
TextureCoordinates="{Binding Pipe.TextureCoordinates}" | |
Diameter="{Binding ElementName=PipeDiamSlider, Path= Value }" | |
Material="{Binding Pipe.Material}" | |
BackMaterial="{Binding Pipe.Material}" | |
ThetaDiv="50" IsPathClosed="False" | |
Visible="{Binding ElementName=PipeIsVisibleCheck, Path=IsChecked}"/> | |
<h:TubeVisual3D x:Name="WellVisual" Path="{Binding Well.Path}" | |
TextureCoordinates="{Binding Well.TextureCoordinates}" | |
Diameter="{Binding ElementName=WellDiamSlider,Path= Value}" | |
Material="{Binding Well.Material}" | |
BackMaterial="{Binding Well.Material}" | |
ThetaDiv="50" IsPathClosed="False" | |
Visible="{Binding ElementName=WellIsVisibleCheck, Path=IsChecked}"/> | |
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength}" MajorDistance="{Binding MajorGridSpacing}" | |
Visible="{Binding IsChecked, ElementName=RenderGridCheckBox}" Thickness="{Binding ElementName=GridSlider, Path=Value }" | |
MinorDistance="{Binding MinorGridSpacing}" LengthDirection="1,0,0" Normal="0,0,1" | |
Center="{Binding BottomPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="{Binding SelectedColorText, ElementName=BackPlaneColor}" /> | |
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength}" LengthDirection="0,0,1" Normal="1,0,0" | |
Visible="{Binding IsChecked, ElementName=RenderGridCheckBox}" Thickness="{Binding ElementName=GridSlider, Path=Value }" | |
MajorDistance="{Binding MajorGridSpacing}" MinorDistance="{Binding MinorGridSpacing}" | |
Center="{Binding BackLeftPlaneCenter, UpdateSourceTrigger=PropertyChanged}" Fill="{Binding SelectedColorText, ElementName=BackPlaneColor}" /> | |
<h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength}" LengthDirection="1,0,0" Normal="0,1,0" | |
Visible="{Binding IsChecked, ElementName=RenderGridCheckBox}" Thickness="{Binding ElementName=GridSlider, Path=Value }" | |
MajorDistance="{Binding MajorGridSpacing}" MinorDistance="{Binding MinorGridSpacing}" | |
Center="{Binding BackRightPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="{Binding SelectedColorText, ElementName=BackPlaneColor}" /> | |
<h:BillboardTextGroupVisual3D Background="Gray" Foreground="White" FontSize="12" Offset="2,2" | |
Padding="1" Items="{Binding GridLabels, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" | |
IsEnabled="{Binding IsChecked, ElementName=FreezeAxisLblsCheckBox}" /> | |
<h:BillboardTextGroupVisual3D Background="Black" Foreground="White" FontSize="10" Offset="3,3" | |
Padding="2" Items="{Binding MarkerLabels, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" /> | |
</h:HelixViewport3D> | |
</DockPanel> | |
</Grid> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment