TRay


Croquet-Teapot

Comment:

TRay specifies a position and orientation, usually inherited from it's parent TFrame. Though you can modify this. It is used to determine the closest object in a particular direction and the distance to that object. 

This is a factored TPointer change set. The new TRay class is concerned only with picking an object, while the new TPointer class is a subclass of TRay, but includes the events model. This means that the TCamera uses a TRay for the downPointer, and it also makes it easier to use a TRay for arbitrary objects looking for the floor. I also completely rewrote the actual matrix math. Essentially, a TRay is a TFrame with the ability to pick. Now it actually uses the local and global orientation matrices to perform the test. 

TRay tests are fairly expensive, so be careful with them. Also, I don't think you want to use a translation with them. I think more work needs to be done to make this robust.

The way a TRay works is you simply place it as a child frame, and it will automatically get called at the next render loop. You can use it directly by calling the following methods:

#pointerPick: or 
#pointerPickFloor: 

with a TBoundSphere as the argument (which in turn points to a TFrame) as the argument. If the return is true, then the TRay intersected the object.

You can also call one of the #frame:xxxx:xxxx: messages if you know the type of object you are attempting to select. If you wish to check a number of objects and keep only the closest, call the message #testDistance:false. Make sure you call #testDistance: true when you are done.

The following variables are for internal use only:
	framePointer - is the ray orientation relative to the object we are testing against.
	framePosition - is the ray location relative to the object we are testing against.
	sphereDistSquared - is the distance to the recently tested sphere. 
	currentFrame - this is the frame we are currently testing against.
	doSelect - turns the selection of the TRay on/off. This is used by the TPointer to keep the pointer from selecting other objects between a mouseDown/mouseUp.
	testDistance - a boolean that allows us to ignore the current distance when testing for object intersection. It is used when the user needs to determine intersection and to a specific object.
	downRay - boolean indicates whether this is a floor test ray or not.
	automatic - indicates whether a ray is tested automatically during rendering.
The following variables are for external use:

	selectedObject - this is the object that owns the selected object. It may be the same, but doesn't have to be.
	selectedFrame - this is the frame object that is selected.
	selectedFramePosition - this is the ray location relative to the selected object.
	selectedParent - this is the parent of the selectedFrame at the time of rendering/selection.
	selectedIndex - this may be used by the selectedObject. It is usually the surface index.
	selectedPoint - this is the selected point in the local selectedFrame reference.
	selectedNormal - this is the surface normal at the selectedPoint.
	selectedDistance - this is the distance from the ray position.
	selectedDistanceSquared - this is the squared distance from the ray position.
	
	lastSelectedObject - previously completed selectedObject
	lastSelectedFrame - previously completed selectedFrame
	lastSelectedPoint - previously completed selectedPoint

Hierarchy:

ProtoObject
Object
TObject
TFrame
TRay

Summary:

instance variables:

automatic currentFrame doSelect downRay framePointer framePosition lastSelection maxDistance maxDistanceSquared minDistance minDistanceSquared selection sphereDistSquared testDistance

class variables:

TDownRay

methods:

instance class
accessing initialize picking testing class initialization

Detail:

instance variables:

automatic
currentFrame
doSelect
downRay
framePointer
framePosition
lastSelection
maxDistance
maxDistanceSquared
minDistance
minDistanceSquared
selection
sphereDistSquared
testDistance

class variables:

TDownRay

instance methods:

accessing
automatic


	^ automatic.
copiedSelection

	| result |
	result _ selection copy.
	^result.
currentFrame


	^ currentFrame.
currentFrame: cf


	currentFrame _ cf.
	framePointer _ nil. " framePointer is now invalidated, so clear it "
	framePosition _ nil. " framePosition is also crap."
doSelect


	^ doSelect.
doSelect: bool


"Turns the ray test on/off so that the ray test will be focused on a selected item. This is done when we have a mouseDown and want to track the selected object."
	doSelect _ bool.
downRay


	^ downRay.
downRay: bool


	downRay _ bool.
framePointer

	| inv |
	framePointer ifNil:[
		currentFrame ifNotNil:[
			inv _ currentFrame inverseGlobalTransform.
			self framePosition: (inv localPointToGlobal: self globalPosition).].
			self framePointerTrans: currentFrame globalOrientation.
		].
	^ framePointer.
framePointer: fp

	
	framePointer _ fp.
framePointerTrans: trans


	" convert from global to local target frame coordinates. "
	framePointer _ self globalTransform transposed composeWith: trans.
framePosition

	self framePointer.
	^ framePosition.
framePosition: fp


	framePosition _ fp.
lastSelection

	^lastSelection
maxDistance


	^ maxDistance.
maxDistance: md


	maxDistance _ md.
	maxDistanceSquared _ md*md.
minDistance


	^ minDistance.
minDistance: md


	minDistance _ md.
	minDistanceSquared _ md*md.
resetSelected

	| lastFrame frame |
	frame := self selectedObject.
	lastFrame := self lastSelection object.
	doSelect ifTrue:[
	lastFrame ~= frame ifTrue:[
" ------ pointer has left the lastFrame "
		lastFrame ifNotNil:[
			lastFrame isComponent ifTrue:[
				(lastFrame handlesPointerOver: self) ifTrue:[
					lastFrame pointerLeave: self.
					].
				].
			].
" ------ pointer has entered the frame "
		frame ifNotNil:[
			frame isComponent ifTrue:[
				(frame handlesPointerOver: self) ifTrue: [
					frame pointerEnter: self.
				].
			].
		].
	] ifFalse: [
" ------ pointer is still inside the same object "
		frame ifNotNil:[
			(frame handlesPointerOver: self) ifTrue: [
				frame pointerOver: self.
			].
		].
	].
	lastSelection := selection.
	selection := TSelection new.
	selection distance: Float infinity.
	] ifFalse: [
	currentFrame _ self selectedFrame.
	framePosition _ nil.
	framePointer _ nil.].
	self maxDistance: Float infinity.
resetTotal

	lastSelection := TSelection new.
	selection := TSelection new.
	selection distance: Float infinity.
	self maxDistance: Float infinity.
selected: sel

	selection := sel.
selectedDistance

	
	^ selection distance.
selectedDistance: dist


	selection distance: dist* Croquet world frameScale.
selectedDistanceSquared

	
	^ selection distanceSquared.
selectedDistanceSquared: dist


	selection distanceSquared: dist* Croquet world frameScaleSquared.
selectedFrame

	^ selection frame
selectedFrame: sf

	selection frame: sf
selectedFramePosition


	^selection framePosition.
selectedFramePosition: sfp


	selection framePosition: sfp.
selectedIndex

	^ selection index
selectedIndex: si

	selection index: si
selectedNormal

	^ selection normal
selectedNormal: sn

	selection normal: sn.
selectedObject

	^ selection object
selectedObject: so


	selection object: so.
selectedParent

	^ selection parent
selectedParent: sp

	selection parent: sp.
selectedParentTransform

	^ selection parentTransform.
selectedParentTransform: spt

	selection parentTransform: spt.
selectedPoint

	^ selection point
selectedPoint: sp

	selection point: sp.
selectedPointAt

	^ (self selectedPoint - self selectedFramePosition)normalized.
selectedTriangle

	^selection triangle
selectedTriangle: aTriangle

	selection triangle: aTriangle
selection

	^selection
setAutomatic: bool


	automatic _ bool.
sphereDistSquared


	^ sphereDistSquared.
sphereDistSquared: sds

	sphereDistSquared _ sds.
testDistance


	^ testDistance.
testDistance: bool


	testDistance _ bool.
testSelectedDistance: dist

 
	| scaledDistance |
	self testDistance ifFalse:[ ^ true. "don't test, just say yes."].
	dist < 0  ifTrue:[^ false.].
	scaledDistance _ dist * Croquet world frameScale.
	selection distance < scaledDistance ifTrue:[^ false ]. "we already have a closer point."
	minDistance > scaledDistance ifTrue:[^ false.]. "this is too close"
	maxDistance < scaledDistance ifTrue:[^ false.]. "this is too far"
	^ true.
testSelectedDistanceSquared: dist


	| scaledDistance |
	self testDistance ifFalse:[ ^ true. "don't test, just say yes."].
	scaledDistance _ Croquet world frameScaleSquared * dist.
	selection distanceSquared < scaledDistance ifTrue:[^ false ]. "we already have a closer point."
	minDistanceSquared > scaledDistance ifTrue:[^ false]. "this choice is too close "
	maxDistanceSquared < scaledDistance ifTrue:[^ false]. "this choice is too far "
	^ true.

initialize
initialize

	super initialize.

	selection := TSelection new.
	lastSelection := TSelection new.
	self doSelect: true.
	self testDistance: true.
	self visible: false.
	self downRay: false.
	self resetSelected.
	self setAutomatic: true.
	self minDistance: 0.0.
	self maxDistance: Float infinity.
	^self

picking
frame: frame pickCylinderFrom: pnt1 radius: rad1 to: pnt2 radius: rad2

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickCylinderFrom: pnt1 radius: rad1 to: pnt2 radius: rad2.
	self testDistance: true.
	^ rval.
frame: frame pickCylinderFrom: pnt1 to: pnt2 radius: rad

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickCylinderFrom: pnt1 to: pnt2 radius: rad.
	self testDistance: true.
	^ rval.
frame: frame pickPlane: position normal: normal

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickPlane: position normal: normal.
	self testDistance: true.
	^ rval.
frame: frame pickQuad: norm q1: p1 q2: p2 q3: p3 q4: p4

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickQuad: norm q1: p1 q2: p2 q3: p3 q4: p4.
	self testDistance: true.
	^ rval.
frame: frame pickQuad: p1 q2: p2 q3: p3 q4: p4

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickQuad: p1 q2: p2 q3: p3 q4: p4.
	self testDistance: true.
	^ rval.
frame: frame pickSphere: loc radiusSquared: rs


	| rval |
	
	self testDistance: false.
	self currentFrame: frame.
	rval _ self pickSphere: loc radiusSquared: rs.
	self testDistance: true.
	^ rval

frame: frame pickTriangle: p1 tri: p2 tri: p3

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickTriangle: p1 tri: p2 tri: p3.
	self testDistance: true.
	^ rval.
frame: frame pickTriangle: norm tri: p1 tri: p2 tri: p3

	| rval |
	self testDistance: false.
	self currentFrame: frame. 
	rval _ self pickTriangle: norm tri: p1 tri: p2 tri: p3.
	self testDistance: true.
	^ rval.
globalTransformUpdate

	
	|trans|

	downRay ifTrue:[
		trans _ globalPosition.
		globalTransform _ TDownRay clone.
		globalTransform translation: trans.
	].
pick: bnds


	| position |

	bnds ifNil:[^ false ].
	bnds frame ifNotNil:[
		bnds frame isSolid ifTrue:[
			self downRay ifFalse:[ self pointerPick: bnds.] 
			ifTrue:[
				position _ (bnds globalPosition - self globalPosition).
				position *= position.
				(position x + position z < bnds radiusSquared) ifTrue:[
					(self pickDownSphere: bnds) ifTrue:[
						(self pointerPickFloor: bnds frame) ifTrue:[ ^ true. ].
					].
				].
			].
		].
	].
	^ false.
pickBoundSphere: bnds
 
	| position dp d gpAt |

	gpAt _ self globalTransform column3.
	position _ bnds globalPosition - self globalPosition.
	dp _ position - ((gpAt dot: position)*gpAt).
	d _ dp dot: dp.
	d < bnds radiusSquared ifTrue:[
		self sphereDistSquared: d.
		^ true.].
	^ false.
pickCylinderFrom: pnt1 radius: rad1 to: pnt2 radius: rad2


	| p1 p2 r1 r2 vn lv lx dx pv n nvn pd v ht cosCone cosPlane ip d1 d2 dd |

	rad1 = rad2 ifTrue: [ 
	"right cylinder"
	^ self pickCylinderFrom: pnt1 to: pnt2 radius: rad1.
	]
	ifFalse: [ "cone"
" Figure out where the vertex is.
	r1,r2 - top and bottom radii. r2 > r1
	p1,p2 - top and bottom center points.
	v - vertex of the cone
	ht - true height of the cone
	vn - vertex normal (up-down normal)
	n - normal plane vector
	nvn - vertex normal dot with plane normal
	ip - actual intersection point"

	rad2 < rad1 ifTrue:[
		r1 _ rad2.
		r2 _ rad1.
		p1 _ pnt2 - self framePosition.	
		p2 _ pnt1 - self framePosition.]
	ifFalse:[
		r1 _ rad1.
		r2 _ rad2.
		p1 _ pnt1 - self framePosition.
		p2 _ pnt2 - self framePosition.].

	rad1 = 0 ifTrue:[ 
		v _ p1.
		ht _ (p1- p2) length.
		vn _ (p2 - p1) normalized.
		] 
	ifFalse:[
		vn _ (p2 - p1) normalized.
		pd _ (p1-p2)length.
		ht _ (r2*pd)/(r2-r1).
		v _ p2 - (vn * ht).
		].


" Calculate the plane that cuts the cone. This is from Graphics Gems V pg 227."
	n _ self framePointer row3 cross: v.
	lv _ n length.
	lv = 0 ifTrue:[^ false.] ifFalse:[ n _ n/lv.].
" We hit the vertex point."
	(n dot: n)=0 ifTrue:[
		ip _ v.]
	ifFalse:[
" Test the plane to see if it intersects the cone."
		cosCone _ ht/(((ht*ht) + (r2*r2))sqrt).
		nvn _ n dot: vn.
		cosPlane _ (1- (nvn * nvn)) sqrt.
		cosCone > cosPlane ifTrue:[ ^ false. "no intersection."].

		lx _ n dot: (p2-v). " distance of p2 from plane "
		dx _ (lx*lx)/ht. " difference in height of closest point to p2"
		lv _ (p2-v) - (n * lx). " actual location of closest point of p2 to plane "
		pv _ v+ (lv * (ht/(ht-dx))). " location of this point on the plane defined by p2. "

		dx_((r2*r2)-((pv-p2)squaredLength))sqrt. " distance from pv to r2 on circle at p2"
		pv _ pv + (dx * ((n cross: vn) normalized)).

		pv_(pv-v)normalized. "vector from v ON THE CYLINDER"

		d1 _ self framePointer row3.
		d2 _ pv.
		dd _ d1 cross: d2.
		dx _ dd dot: dd.
		dx = 0 ifTrue:[^false].
		dx _ ( (v cross: d2) dot: dd )/dx.
		(self testSelectedDistance: dx abs) ifFalse:[^ false].
		ip _ self framePosition + (d1*dx).
		].

" Now that we (finally) have ip - the intersection of the pointer with the -infinite- cylinder, we need to test to see if it is between the top and bottom planes,"
	((ip - pnt1) dot: vn)*((ip-pnt2) dot: vn) > 0 ifTrue:[ 
		"the point is outside the bounds"
		^ false.].
	self selectedPoint: ip.
	self selectedDistance: dx abs.
	^ true.
	].
	

	
	
pickCylinderFrom: pnt1 to: pnt2 radius: rad


	| p1 p2 pd d ps s pn pa dd ip |

" 	p1, p2 axis points of the cylinder relative to the framePosition.
	pd - the cutting plane. The ray is on this plane, which is parallel to the cylinder axis.
	d - distance of plane from cylinder axis.
	s - distance from d point (p1 + d*p) to cylinder surface.
	ps - vector on plane, perpendicular to axis.
	pn - normal vector.
	ip - selected point"

	p1 _ pnt1 - self framePosition.
	p2 _ pnt2 - self framePosition.

" Calculate the cutting plane between the ray and parallel to the axis of the cylinder. "
	pd _ (self framePointer row3 cross: (p1-p2)).
" Test if we ray is parallel to axis."
	(pd dot: pd)=0 ifTrue:[ ^ false.].
	pd normalize.

" Test to see if the plane (and hence the ray) distance is less than the rad distance to the center of the cylinder."
	d _ pd dot: p1.
	d abs >rad ifTrue:[ ^ false.]. 

" Calculate the perpendicular normal to the axis on the plane."
	s _ ((rad*rad)-(d*d))sqrt.
	ps _ ((p1-p2) cross: pd) normalized.

" Calculate the normal on the cylinder at the intersection point."
	pn _ ((s*ps)-(d*pd)).
	
" Now we just calculate the intersections between two lines."
	p1 _ p1 + pn.
	p2 _ p2 + pn.
	pn normalize.

	pa _ (p2-p1) normalized.

" We know that the two lines can't be parallel because we tested earlier."
	dd _ (self framePointer row3) cross: pa.
	
	s_ (((p1 cross: pa) dot: dd)/(dd dot: dd)).
	ip _ framePointer row3 * s.

" Test if we are between the two ends of the cylinder."
	((ip-p1) dot: pa)*((ip-p2) dot: pa)>0 ifTrue:[
		"The point is outside the bounds"
		^ false.].
	s _ s abs.
	(self testSelectedDistance: s) ifFalse:[^false].
	self selectedDistance: s.
	self selectedPoint: (ip + self framePosition).
	self selectedNormal: pn.
	^ true.
	
	





pickDownSphere: bnds
 
	| position |

	position _ bnds globalPosition - self globalPosition.
	^ (((position x *position x) + (position z*position z)) < bnds radiusSquared).
pickFrame: framePtr tri: p1 tri: p2 tri: p3



" This method works by using the orthogonal picking matrix as orthogonal planes going through the origin at 'origin'. The points of the triangle are tested to the two planes orthoganal to the direction of picking. If they are on either side of both planes, then there is a very high probability that our picking ray interesects the triangle, and we do the actual calculation of the intersection point. Note the comments below. I use a vertical bar to indicate which side of the plane a point is on.  ***** p1 p2 | ***** means that p1 and p2 are on the same side, 
***** p1 | p2 ***** indicates that they are on opposite sides. p1 is always on the left side, because it is the first one I test. This really just means - same side as p1 - no meaning other than that. 

This can be optimized for tri-strips, tri-fans, and even full face based meshes. When I have the time... "

	| d1 d2 d3 pp1 pp2 dd1 dd2 xPlane yPlane  |
		
	xPlane _ framePtr row1.
	d1 _ xPlane dot: p1.
	d2 _ xPlane dot: p2.
	d3 _ xPlane dot: p3.
	dd1 _ d1*d2.
	dd2 _ d1*d3.

" test if all points are on the same side of the x plane."
	dd1 < 0 ifFalse:[ 				"***** p1 p2 |  *****"
		dd2 < 0  ifFalse:[  ^ false		"***** p1 p2 p3 | *****" ] 
				ifTrue:[ 			"***** p1 p2 | p3 *****"
						pp1 _ p1 + ((p3-p1) * d1/(d1-d3)).
						pp2 _ p2 + ((p3-p2) * d2/(d2-d3)).]]
		ifTrue:[ 				"***** p1 | p2 *****"
			dd2 < 0  ifTrue:[ 		"***** p1 | p2 p3 *****" 
						pp1 _ p1 + ((p2-p1) * d1/(d1-d2)).
						pp2 _ p1 + ((p3-p1) * d1/(d1-d3)).] 
					ifFalse:[ 		"***** p1 p3 | p2 *****"
						pp1 _ p1 + ((p2-p1) * d1/(d1-d2)).
						pp2 _ p3 + ((p2-p3) * d3/(d3-d2)).].].
" At this point, the triangle is intersected by the x plane and we know where. If both points are on the same side of the y plane, we are done. "
	yPlane _ framePtr row2.

	d1 _ yPlane dot: pp1.
	d2 _ yPlane dot: pp2.
" test if both points are on the same side of the y plane."
	d1 * d2 < 0 ifFalse:[ 
	 ^ false ].
	pp1 _ pp1 + ((pp2-pp1) *  d1/ (d1-d2)).
	d1 _ pp1 squaredLength.
	(self testSelectedDistanceSquared: d1) ifTrue:
		[
			self selectedDistanceSquared: d1.
			self selectedPoint: pp1.
			^ true.
		].

	^ false.

	
	


	


	
pickLocalBoundSphere: bnds
 
	| lpAt position dp dd |

	lpAt _ self framePointer row3.
	position _ bnds localPosition - self framePosition.
" project center of sphere onto the ray"
	dp _ position - (lpAt * (lpAt dot: position)).
	dd _ dp dot: dp.

" distance of ray to center - if greater than radius, return false, otherwise, the ray intersects."
	^ (dd < bnds radiusSquared)

pickLocalNorm: norm
 

	^ (self framePointer row3 dot: norm) < 0.
pickPlane: position normal: normal


	| div sp d |

	div _ normal dot: self framePointer row3. 
	div = 0 ifTrue:[ ^ false ].
	d _ ((position-self framePosition) dot: normal)/div.
	sp_ self framePosition +  (self framePointer row3 * d).
	
	(self testSelectedDistance: d) ifFalse:[^false].
	self selectedDistance: d.
	self selectedPoint: sp.
	self selectedNormal: normal.
	^ true.
pickQuad: norm q1: p1 q2: p2 q3: p3 q4: p4


	| pointAt po1 po2 po3 po4 d1 d2 d3 d4 |

	pointAt _ self framePointer row3.
	norm ifNotNil:[
		((pointAt dot: norm) < 0) ifTrue:[^ false ].].
	po1 _ self framePosition - p1.
	po2 _ self framePosition - p2.
	po3 _ self framePosition - p3.
	po4 _ self framePosition - p4.
	d1 _ pointAt dot: po1.
	d2 _ pointAt dot: po2.
	d3 _ pointAt dot: po3.
	d4 _ pointAt dot: po4.
" Are any of the points actually in front of the pointer?"
	d1 < 0 ifTrue:[
		d2 < 0 ifTrue:[
			d3 < 0 ifTrue:[
				d4 < 0 ifTrue:[^ false.]]]].

"	(norm dot: po1) > 0 ifTrue:[ ^ false.]."

" If all of the points are further than the current selected distance, then we are done."
	(self testSelectedDistance: d1) ifFalse:[
	(self testSelectedDistance: d2) ifFalse:[
	(self testSelectedDistance: d3) ifFalse:[
	(self testSelectedDistance: d4) ifFalse:[^ false]]]].

" At some point, I need to write a special pickFrame for quads, because I calculate one of the vertices twice this way. In Squeak, every cycle counts!."
  	(self pickFrame: self framePointer tri: po1 tri: po2 tri: po3) ifFalse:[ 
		(self pickFrame: self framePointer tri: po3 tri: po4 tri: po1) ifFalse:[^ false]].
	self selectedPoint: (self framePosition - self selectedPoint).
	self selectedNormal: norm.
	^ true.
	
pickQuad: p1 q2: p2 q3: p3 q4: p4


	^ self pickQuad: nil q1: p1 q2: p2 q3: p3 q4: p4.
	
pickSphere: loc radiusSquared: rs


	| p dp d dd c |

" This method is used to find the surface of the actual sphere, not just determine if the pointer intersects it."
	rs = 0.0 ifTrue:[^ false.].
	p _ loc - self framePosition.
	
" project center of sphere onto the ray"
	dp _ self framePointer row3 * (self framePointer row3 dot: p).
	dd _ dp - p.
" distance of ray to center - if greater than radius, exit"
	d _ dd dot: dd.
	d > rs ifTrue:[^false.].
" calculate the point on the sphere"
	c _ (rs-d)sqrt.
	dp _ dp + (c * self framePointer row3).
	d _ dp dot: dp.
" if it is closer than the current selected object, save and calc norm."
	(self testSelectedDistanceSquared: d) ifTrue: [
		self selectedDistanceSquared: d.
		self selectedPoint: (self framePosition + dp).
		self selectedNormal: (self selectedPoint - loc) normalized.
		^ true.		
		].
	^ false.
pickTriangle: p1 tri: p2 tri: p3


	^ self pickTriangle: nil tri: p1 tri: p2 tri: p3.
pickTriangle: norm tri: p1 tri: p2 tri: p3


	| pointAt po1 po2 po3 d1 d2 d3 |

	pointAt _ self framePointer row3.
	norm ifNotNil:[
		((pointAt dot: norm) < 0) ifTrue:[^ false ].].
	po1 _ self framePosition - p1.
	po2 _ self framePosition - p2.
	po3 _ self framePosition - p3.

" Are any of the points actually in front of the pointer?"
	d1 _ pointAt dot: po1.
	d2 _ pointAt dot: po2.
	d3 _ pointAt dot: po3.
	d1 < 0 ifTrue:[
		d2< 0 ifTrue:[
			d3 < 0 ifTrue:[^ false.]]].

" If all of the points are further than the current selected distance, then we are done."
	(self testSelectedDistance: d1) ifFalse:[
	(self testSelectedDistance: d2) ifFalse:[
	(self testSelectedDistance: d3) ifFalse:[^ false]]].

	(self pickFrame: self framePointer tri: po1 tri: po2 tri: po3) ifTrue: [
		self selectedPoint: (self framePosition - self selectedPoint).
		self selectedNormal: norm.
		self selectedTriangle: (Array with: p1 with: p2 with: p3).
		^ true.].
	 ^ false.
pickTriangles: vertices list: triList

	"Pick an indexed triangle array"
	| pointAt po1 po2 po3 d1 d2 d3 fp index v0 v1 v2 |
	fp := self framePosition.
	pointAt _ self framePointer row3.
	index := 0.
	[index < triList size] whileTrue:[
		v0 := vertices at: (triList at: (index := index+1))+1.
		v1 := vertices at: (triList at: (index := index+1))+1.
		v2 := vertices at: (triList at: (index := index+1))+1.
		po1 := fp - v0.
		po2 := fp - v1.
		po3 := fp - v2.

		"Are any of the points actually in front of the pointer?"
		d1 _ pointAt dot: po1.
		d2 _ pointAt dot: po2.
		d3 _ pointAt dot: po3.
		(d1 >= 0.0 or:[d2 >= 0.0 or:[d3 >= 0.0]]) ifTrue:[
			" If all of the points are further than the current selected distance, then we are done."
			((self testSelectedDistance: d1) or:[
				(self testSelectedDistance: d2) or:[
					(self testSelectedDistance: d3)]]) ifTrue:[

				(self pickFrame: self framePointer tri: po1 tri: po2 tri: po3) ifTrue: [
					self selectedPoint: (self framePosition - self selectedPoint).
					self selectedNormal: nil.
					self selectedTriangle: (Array with: v0 with: v1 with: v2).
					^ true.
				].
			].
		].
	].
	^false
pointerPick: bnds

	| position frame gpAt dp d |
	doSelect ifTrue:[
		bnds ifNil:[^ false.].
		bnds frame objectOwner isComponent ifTrue:[
			position _ bnds globalPosition - self globalPosition.
			self selectedDistance > ((position length - bnds radius) * Croquet world frameScale) ifTrue:[
				gpAt _ self globalTransform column3.
				dp _ position - ((gpAt dot: position)*gpAt).
				d _ dp dot: dp.
				d < bnds radiusSquared ifFalse:[ ^ false ].
				self sphereDistSquared: d.
				frame _ bnds frame.
				self currentFrame: frame.
				(frame pick: self) ifTrue:[
					self selectedPoint ifNil:[self selectedPoint: B3DVector3 new.].
					self selectedFrame: frame.
					self selectedParent: frame parent.
					self selectedFramePosition:  self framePosition.
					self selectedObject: frame objectOwner.
					self selectedParentTransform: self parent globalTransform.
					^ true.
					].
				].
			].
		].
	^ false.
pointerPickFloor: frame

	
	self currentFrame: frame.
	(frame pickFloor: self) ifTrue:[
		self selectedFrame: frame.
		self selectedParent: frame parent.
		self selectedFramePosition:  self framePosition.
		self selectedObject: frame objectOwner.
		^ true.
		].
	^ false.
pointerPickTree: frame


	frame visible ifTrue:[(self pointerPick: frame boundSphere) ifTrue:[^ true].].
	frame frameChildren ifNotNil:[
		frame frameChildren do:[ :fc | (self pointerPickTree: fc) ifTrue:[^ true.].].].
	^ false.
portalTest: frame at: at

	| rval saveFP up side |

	saveFP _ framePointer.
	rval _ false.

	up _ B3DVector3 x: 0.0 y:1.0 z:0.0.
	side _ (up cross: at) normalized.
	up _ (at cross: side) normalized.
	framePointer _ B3DMatrix4x4 identity.
	framePointer at: 1 at: 1 put: side x.
	framePointer at: 1 at: 2 put: side y.
	framePointer at: 1 at: 3 put: side z.
	framePointer at: 2 at: 1 put: up x.
	framePointer at: 2 at: 2 put: up y.
	framePointer at: 2 at: 3 put: up z.
	framePointer at: 3 at: 1 put: at x.
	framePointer at: 3 at: 2 put: at y.
	framePointer at: 3 at: 3 put: at z.

	self testDistance: false.
	(frame pick: self) ifTrue:[ rval _ true.].
	self testDistance: true.
	framePointer _ saveFP.
	^ rval.

testing
isRay


	^ true.

class methods:

class initialization
initialize


	TDownRay _ B3DMatrix4x4 identity rotationAroundX: -90.

^top


- made by Dandelion -