I didn't find a solution to actually picking the face of my solid. Instead I made a jig and used some geometry to find a point on the edge of the solid.
Basically it explodes the solid , then in the jig it determins if your cursor is in front of one of the edges, then projects a point onto the edge. This may not help you, but it was good enough for me.
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.Geometry
Namespace TimberTools
Namespace AcadJigs
Class SinkToCounterJig
Inherits EntityJig
Private _ActualPoint As Point3d
Private _Counter As Solid3d
Private _Sink As BlockReference
Private _x As TreeNode
Private _BasePoint As Point3d
Private _Offset As Double
Private _BasePointOffest As Vector3d
Private _Vector As Vector3d
Public ReadOnly Property SelectedPoint() As Point3d
Get
Return _ActualPoint
End Get
End Property
Public ReadOnly Property Vector() As Vector3d
Get
Return _Vector
End Get
End Property
Public Sub New(ByVal BlockRef As BlockReference, ByVal Counter As Solid3d, ByVal BasePoint As Point3d, ByVal Offset As Double)
MyBase.New(BlockRef)
_Sink = BlockRef
_Counter = Counter
_BasePoint = BasePoint
_Offset = Offset
_BasePointOffest = _BasePoint.GetVectorTo(_Sink.Position)
'#collect tree of expoded object. Was having trouble
' with acad crashes when this was all in the update routine
_x = New TreeNode
Dim ents As New DBObjectCollection
'#explode wall
_Counter.Explode(ents)
For Each obj As DBObject In ents
Dim ent As New TreeNode
ent._Item = obj
If obj.GetType() Is GetType(Region) Then
'#Explode Region
Dim Lines As New DBObjectCollection
CType(obj, Region).Explode(Lines)
For Each line As DBObject In Lines
Dim Ln As New TreeNode
Ln._Item = line
ent._Children.Add(Ln)
Next
Lines.Clear()
Lines.Dispose()
End If
_x._Children.Add(ent)
Next
ents.Clear()
ents.Dispose()
End Sub
Public Sub CleanUp()
CleanUp(_x)
End Sub
Private Sub CleanUp(ByVal node As TreeNode)
If node._Item IsNot Nothing Then
If GetType(DBObject).IsAssignableFrom(node._Item.GetType()) Then
CType(node._Item, DBObject).Dispose()
End If
End If
For Each nd As TreeNode In node._Children
CleanUp(nd)
Next
End Sub
Protected Overloads Overrides Function Sampler(ByVal prompts As JigPrompts) As SamplerStatus
Dim jigOpts As New JigPromptPointOptions()
jigOpts.UserInputControls = (UserInputControls.Accept3dCoordinates Or UserInputControls.NoZeroResponseAccepted Or UserInputControls.NoNegativeResponseAccepted)
jigOpts.Message = vbLf & "Pick front side of counter top: "
jigOpts.BasePoint = _BasePoint
jigOpts.UseBasePoint = True
Dim dres As PromptPointResult = prompts.AcquirePoint(jigOpts)
If _ActualPoint = dres.Value Then
Return SamplerStatus.NoChange
Else
_ActualPoint = dres.Value
End If
Return SamplerStatus.OK
End Function
Protected Overloads Overrides Function Update() As Boolean
Try
Dim NewLocation As Point3d = _Sink.Position
Dim Closest As Point3d = _Sink.Position
For Each RegNode As TreeNode In _x._Children
'#only look at regions (shouldn't be anything else)
Dim Reg As Region = RegNode._Item
'Exclude top and bottom regions of counter
If Reg.Normal.Z = 0 Then
'# find coordinates by examining lines
Dim P1 As Point3d
Dim P2 As Point3d
'# get lines of region
For Each lineNode As TreeNode In RegNode._Children
Dim line As Line = lineNode._Item
'Get horizontal lines
If line.StartPoint.Z = line.EndPoint.Z Then
P1 = line.StartPoint
P2 = line.EndPoint
End If
Next
Dim v1 As Vector3d = P1.GetVectorTo(_ActualPoint)
Dim v2 As Vector3d = P2.GetVectorTo(_ActualPoint)
Dim a1 As Double = Reg.Normal.GetAngleTo(v1)
Dim a2 As Double = Reg.Normal.GetAngleTo(v2)
Dim a3 As Double = v1.GetAngleTo(v2)
'< 45
If (a1 < 0.25 * Math.PI Or a2 < 0.25 * Math.PI) Or _
a3 > 0.5 * Math.PI And a1 < 0.5 * Math.PI And a2 < 0.5 * Math.PI Then
'Pointer in view of this region
Dim Loc As Point3d = _BasePoint.OrthoProject(New Plane(P1, Reg.Normal))
Dim Vec As Vector3d = _BasePoint.GetVectorTo(Loc).GetNormal
_Vector = Vec
Dim FinalPos As Point3d = Loc.Subtract(Vec.MultiplyBy(_Offset))
NewLocation = FinalPos.Add(_BasePointOffest)
If _ActualPoint.DistanceTo(NewLocation) < _ActualPoint.DistanceTo(Closest) Then
Closest = NewLocation
End If
End If
End If
Next
If Closest <> _Sink.Position Then
_Sink.Position = Closest
End If
Return True
Catch generatedExceptionName As System.Exception
Return False
End Try
Return True
End Function
Public Function GetEntity() As Entity
Return Entity
End Function
Private Class TreeNode
Public _Children As List(Of TreeNode)
Public _Item As Object
Sub New()
_Children = New List(Of TreeNode)
End Sub
End Class
End Class
End Namespace
End Namespace