'From Squeak 1.19d of April 13, 1997 on 29 June 1997 at 5:15:25 pm'! "Change Set: Graphics3d-Tools Date: 26 June 1997 Author: Andreas Raab and Stefan Schlechtweg Basic tools for 3d Graphics such as Vectors (3d/4d), Matrices, and Rotations."! Object subclass: #Matrix4x4 instanceVariableNames: 'a11 a12 a13 a14 a21 a22 a23 a24 a31 a32 a33 a34 a41 a42 a43 a44 ' classVariableNames: '' poolDictionaries: '' category: 'Graphics3d-Tools'! Object subclass: #Rotation instanceVariableNames: 'a b c d ' classVariableNames: '' poolDictionaries: '' category: 'Graphics3d-Tools'! Point subclass: #Vector3 instanceVariableNames: 'z ' classVariableNames: 'Black Blue Cyan Gray Green Magenta Orange Red White Yellow ' poolDictionaries: '' category: 'Graphics3d-Tools'! Vector3 subclass: #Vector4 instanceVariableNames: 'w ' classVariableNames: '' poolDictionaries: '' category: 'Graphics3d-Tools'! !Color methodsFor: 'conversions'! asColor ^self! asVector3 ^Vector3 x: self red y: self green z: self blue! ! Matrix4x4 comment: '(c) 1994-1997 Andreas Raab and Stefan Schlechtweg, University of Magdeburg, Germany I represent a general 4x4 transformation matrix commonly used in computer graphics.'! !Matrix4x4 reorganize! ('initialize-release' betaSplineBias:tension: bezier bSpline cardinal catmull identity lagrange polyline zero) ('accessing' at:at: at:at:put: rotation:around: rotation:aroundX:y:z: rotationAroundX: rotationAroundY: rotationAroundZ: scaling: scalingX:y:z: translation translation: translationX:y:z:) ('element-accessing' a11 a11: a12 a12: a13 a13: a14 a14: a21 a21: a22 a22: a23 a23: a24 a24: a31 a31: a32 a32: a33 a33: a34 a34: a41 a41: a42 a42: a43 a43: a44 a44:) ('arithmetic' * + -) ('transforming' inversed postMultiply: preMultiply: rotateBy:around: rotatedAround:by: rotatedAroundXBy: rotatedAroundYBy: rotatedAroundZBy: scaleBy: scaledBy: translateBy: translatedBy: transposed) ('double dispatching' addMatrix4x4: multiplyNormal: multiplyNumber: productFromMatrix4x4: productFromVector3: productFromVector4: subtractMatrix4x4:) ('printing' printOn: storeOn:) ('private' copyValuesFrom: do: getSelectorArray putSelectorArray with:do:) ('solving' inplaceDecomposeLU inplaceHouseHolderTransform: inplaceHouseHolderTransformUnrolled: solve: solveLU:) ('comparing' = hash squaredErrorDistanceTo:) ('testing' isZero) ('converting' asMatrix) ! !Matrix4x4 methodsFor: 'initialize-release'! betaSplineBias: beta1 tension: beta2 "Return the betaSpline base matrix if beta1=1 and beta2=0 then the bSpline base matrix will be returned" "for further information see: Foley, van Dam, Feiner, Hughes 'Computer Graphics: Principles and Practice' Addison-Wesley Publishing Company Second Edition, pp. 505" | b12 b13 delta | b12 := beta1 * beta1. b13 := beta1 * b12. delta := 1.0 / (beta2 + (2.0 * b13) + 4.0 * (b12 + beta1) +2.0). a11 := delta * -2.0 * b13. a12 := delta * 2.0 * (beta2 + b13 + b12 + beta1). a13 := delta * -2.0 * (beta2 + b12 + beta1 + 1.0). a14 := delta * 2.0. a21 := delta * 6.0 * b13. a22 := delta * -3.0 * (beta2 + (2.0 * (b13 + b12))). a23 := delta * 3.0 * (beta2 + (2.0 * b12)). a24 := 0.0. a31 := delta * -6.0 * b13. a32 := delta * 6.0 * (b13 - beta1). a33 := delta * 6.0 * beta1. a34 := 0.0. a41 := delta * 2.0 * b13. a42 := delta * (beta2 + 4.0 * (b12 + beta1)). a43 := delta * 2.0. a44 := 0.0. ^self! bezier "Return the bezier base matrix" "for further information see: Foley, van Dam, Feiner, Hughes 'Computer Graphics: Principles and Practice' Addison-Wesley Publishing Company Second Edition, pp. 505" a11 := -1.0. a12 := 3.0. a13 := -3.0. a14 := 1.0. a21 := 3.0. a22 := -6.0. a23 := 3.0. a24 := 0.0. a31 := -3.0. a32 := 3.0. a33 := 0.0. a34 := 0.0. a41 := 1.0. a42 := 0.0. a43 := 0.0. a44 := 0.0. ^self! bSpline "Return the BSpline base matrix" "for further information see: Foley, van Dam, Feiner, Hughes 'Computer Graphics: Principles and Practice' Addison-Wesley Publishing Company Second Edition, pp. 505" a11 := -1.0 / 6.0. a12 := 3.0 / 6.0. a13 := -3.0 / 6.0. a14 := 1.0 / 6.0. a21 := 3.0 / 6.0. a22 := -6.0 / 6.0. a23 := 3.0 / 6.0. a24 := 0.0 / 6.0. a31 := -3.0 / 6.0. a32 := 0.0 / 6.0. a33 := 3.0 / 6.0. a34 := 0.0 / 6.0. a41 := 1.0 / 6.0. a42 := 4.0 / 6.0. a43 := 1.0 / 6.0. a44 := 0.0 / 6.0. ^self! cardinal "a cardinal spline base matrix - just catmull * 2" "for further information see: Foley, van Dam, Feiner, Hughes 'Computer Graphics: Principles and Practice' Addison-Wesley Publishing Company Second Edition, pp. 505" a11 := -1.0. a12 := 3.0. a13 := -3.0. a14 := 1.0. a21 := 2.0. a22 := -5.0. a23 := 4.0. a24 := -1.0. a31 := -1.0. a32 := 0.0. a33 := 1.0. a34 := 0.0. a41 := 0.0. a42 := 2.0. a43 := 0.0. a44 := 0.0. ^self! catmull "Return the Catmull-Rome base matrix" "for further information see: Foley, van Dam, Feiner, Hughes 'Computer Graphics: Principles and Practice' Addison-Wesley Publishing Company Second Edition, pp. 505" a11 := -0.5. a12 := 1.5. a13 := -1.5. a14 := 0.5. a21 := 1.0. a22 := -2.5. a23 := 2.0. a24 := -0.5. a31 := -0.5. a32 := 0.0. a33 := 0.5. a34 := 0.0. a41 := 0.0. a42 := 1.0. a43 := 0.0. a44 := 0.0. ^self! identity "Return a identity matrix" a11 := 1.0. a12 := 0.0. a13 := 0.0. a14 := 0.0. a21 := 0.0. a22 := 1.0. a23 := 0.0. a24 := 0.0. a31 := 0.0. a32 := 0.0. a33 := 1.0. a34 := 0.0. a41 := 0.0. a42 := 0.0. a43 := 0.0. a44 := 1.0. ^self! lagrange "interpolation of all four points t := 0 -> point0, t := 1/3 -> point1, t := 2/3 -> point2, t := 1 -> point3" a11 :=-9.0 / 2.0. a12 := 27.0 / 2.0. a13 := -27.0 / 2.0. a14 := 9.0 / 2.0. a21 := 18.0 / 2.0. a22 := -45.0 / 2.0. a23 := 36.0 / 2.0. a24 := -9.0 / 2.0. a31 := -11.0 / 2.0. a32 := 18.0 / 2.0. a33 := -9.0 / 2.0. a34 := 2.0 / 2.0. a41 := 2.0 / 2.0. a42 := 0.0 / 2.0. a43 := 0.0 / 2.0. a44 := 0.0 / 2.0. ^self! polyline "Return the polyline base matrix :)" a11 := 0.0. a12 := 0.0. a13 := 0.0. a14 := 0.0. a21 := 0.0. a22 := 0.0. a23 := 0.0. a24 := 0.0. a31 := 0.0. a32 := -1.0. a33 := 1.0. a34 := 0.0. a41 := 0.0. a42 := 1.0. a43 := 0.0. a44 := 0.0. ^self! zero "Return a zero matrix" a11 := 0.0. a12 := 0.0. a13 := 0.0. a14 := 0.0. a21 := 0.0. a22 := 0.0. a23 := 0.0. a24 := 0.0. a31 := 0.0. a32 := 0.0. a33 := 0.0. a34 := 0.0. a41 := 0.0. a42 := 0.0. a43 := 0.0. a44 := 0.0. ^self! ! !Matrix4x4 methodsFor: 'accessing'! at: row at: column "Return the element at position row/column" | index | ( 1 > row or:[ 4 < row]) ifTrue:[ ^self subscriptBoundsError: row]. ( 1 > column or:[ 4 < column]) ifTrue:[ ^self subscriptBoundsError: column]. index := column - 1 * 4 + row. ^self perform: (self getSelectorArray at: index).! at: row at: column put: aNumber "Set the element at position row/column to aNumber" | index | ( 1 > row or:[ 4 < row]) ifTrue:[ ^self subscriptBoundsError: row]. ( 1 > column or:[ 4 < column]) ifTrue:[ ^self subscriptBoundsError: column]. index := column - 1 * 4 + row. ^self perform: (self putSelectorArray at: index) with: aNumber.! rotation: anAngle around: aVector3 "set up a rotation matrix around the direction aVector3" self copyValuesFrom: (Rotation angle: anAngle axis: aVector3) asMatrix! rotation: anAngle aroundX: xValue y: yValue z: zValue "set up a rotation matrix around the direction x/y/z" ^self rotation: anAngle around:(Vector3 with: xValue with: yValue with: zValue)! rotationAroundX: anAngle | rad s c | rad := anAngle degreesToRadians. s := rad sin. c := rad cos. a22 := c. a23 := s negated. a33 := c. a32 := s. ^self! rotationAroundY: anAngle | rad s c | rad := anAngle degreesToRadians. s := rad sin. c := rad cos. a11 := c. a13 := s. a33 := c. a31 := s negated. ^self! rotationAroundZ: anAngle | rad s c | rad := anAngle degreesToRadians. s := rad sin. c := rad cos. a11 := c. a12 := s negated. a22 := c. a21 := s. ^self! scaling: aVector ^self scalingX: aVector x y: aVector y z: aVector z! scalingX: xValue y: yValue z: zValue a11 := xValue. a22 := yValue. a33 := zValue. ^self! translation ^a14@a24@a34 ! translation: aVector ^self translationX: aVector x y: aVector y z: aVector z! translationX: xValue y: yValue z: zValue a14 := xValue. a24 := yValue. a34 := zValue. ^self! ! !Matrix4x4 methodsFor: 'element-accessing'! a11 ^a11! a11: aNumber a11 := aNumber. ^self! a12 ^a12! a12: aNumber a12 := aNumber. ^self! a13 ^a13! a13: aNumber a13 := aNumber. ^self! a14 ^a14! a14: aNumber a14 := aNumber. ^self! a21 ^a21! a21: aNumber a21 := aNumber. ^self! a22 ^a22! a22: aNumber a22 := aNumber. ^self! a23 ^a23! a23: aNumber a23 := aNumber. ^self! a24 ^a24! a24: aNumber a24 := aNumber. ^self! a31 ^a31! a31: aNumber a31 := aNumber. ^self! a32 ^a32! a32: aNumber a32 := aNumber. ^self! a33 ^a33! a33: aNumber a33 := aNumber. ^self! a34 ^a34! a34: aNumber a34 := aNumber. ^self! a41 ^a41! a41: aNumber a41 := aNumber. ^self! a42 ^a42! a42: aNumber a42 := aNumber. ^self! a43 ^a43! a43: aNumber a43 := aNumber. ^self! a44 ^a44! a44: aNumber a44 := aNumber. ^self! ! !Matrix4x4 methodsFor: 'arithmetic'! * anObject ^anObject productFromMatrix4x4: self! + aMatrix4x4 ^self addMatrix4x4: aMatrix4x4! - aMatrix4x4 ^self subtractMatrix4x4: aMatrix4x4! ! !Matrix4x4 methodsFor: 'transforming'! inversed "Return the inverse matrix of the receiver. The following method is probably the slowest one can think of - but I need a matrix inverter NOW!!" | e1 e2 e3 e4 | e1 := self solve: (Vector4 x:1 y:0 z:0 w:0). e2 := self solve: (Vector4 x:0 y:1 z:0 w:0). e3 := self solve: (Vector4 x:0 y:0 z:1 w:0). e4 := self solve: (Vector4 x:0 y:0 z:0 w:1). ^(Matrix4x4 new) a11: e1 x; a12: e2 x; a13: e3 x; a14: e4 x; a21: e1 y; a22: e2 y; a23: e3 y; a24: e4 y; a31: e1 z; a32: e2 z; a33: e3 z; a34: e4 z; a41: e1 w; a42: e2 w; a43: e3 w; a44: e4 w; yourself! postMultiply: aMatrix4x4 "Transform in place by aMatrix4x4" ^self copyValuesFrom: (self * aMatrix4x4) ! preMultiply: aMatrix4x4 "Transform in place by aMatrix4x4" ^self copyValuesFrom: (aMatrix4x4 * self)! rotateBy: angle around: aVector3 self copyValuesFrom: (self * (self class withRotation: angle around: aVector3)). ^self! rotatedAround: aVector by: anAngle ^self * (self class withRotation: anAngle around: aVector)! rotatedAroundXBy: anAngle ^self * (self class withRotationAroundX: anAngle)! rotatedAroundYBy: anAngle ^self * (self class withRotationAroundY: anAngle)! rotatedAroundZBy: anAngle ^self * (self class withRotationAroundZ: anAngle)! scaleBy: aVector3 self copyValuesFrom: (self * (self class withScaling: aVector3)). ^self! scaledBy: aVector ^self * (self class withScaling: aVector)! translateBy: aVector3 self copyValuesFrom: (self * (self class withTranslation: aVector3)). ^self! translatedBy: aVector ^self * (self class withTranslation: aVector)! transposed "Return a transposed copy of the receiver" | matrix | matrix := self class new. matrix a11: a11; a12: a21; a13: a31; a14: a41; a21: a12; a22: a22; a23: a32; a24: a42; a31: a13; a32: a23; a33: a33; a34: a43; a41: a14; a42: a24; a43: a34; a44: a44. ^matrix! ! !Matrix4x4 methodsFor: 'double dispatching'! addMatrix4x4: aMatrix4x4 "Add the receiver and aMatrix4x4." | result | result := Matrix4x4 new. result a11: a11 + aMatrix4x4 a11. result a12: a12 + aMatrix4x4 a12. result a13: a13 + aMatrix4x4 a13. result a14: a14 + aMatrix4x4 a14. result a21: a21 + aMatrix4x4 a21. result a22: a22 + aMatrix4x4 a22. result a23: a23 + aMatrix4x4 a23. result a24: a24 + aMatrix4x4 a24. result a31: a31 + aMatrix4x4 a31. result a32: a32 + aMatrix4x4 a32. result a33: a33 + aMatrix4x4 a33. result a34: a34 + aMatrix4x4 a34. result a41: a41 + aMatrix4x4 a41. result a42: a42 + aMatrix4x4 a42. result a43: a43 + aMatrix4x4 a43. result a44: a44 + aMatrix4x4 a44. ^result! multiplyNormal: aVector3 "Multiply aVector with the receiver" | x y z rx ry rz | x := aVector3 x. y := aVector3 y. z := aVector3 z. rx := (x * a11) + (y * a21) + (z * a31). ry := (x * a12) + (y * a22) + (z * a32). rz := (x * a13) + (y * a23) + (z * a33). ^Vector3 x:rx y: ry z: rz! multiplyNumber: aNumber | result | result :=Matrix4x4 zero. result a11: a11*aNumber. result a12: a12*aNumber. result a13: a13*aNumber. result a14: a14*aNumber. result a21: a21*aNumber. result a22: a22*aNumber. result a23: a23*aNumber. result a24: a24*aNumber. result a31: a31*aNumber. result a32: a32*aNumber. result a33: a33*aNumber. result a34: a34*aNumber. result a41: a41*aNumber. result a42: a42*aNumber. result a43: a43*aNumber. result a44: a44*aNumber. ^result! productFromMatrix4x4: matrix "Multiply a 4x4 matrix with the receiver." | result | result := Matrix4x4 new. result a11: ((matrix a11 * a11) + (matrix a12 * a21) + (matrix a13 * a31) + (matrix a14 * a41)). result a12: ((matrix a11 * a12) + (matrix a12 * a22) + (matrix a13 * a32) + (matrix a14 * a42)). result a13: ((matrix a11 * a13) + (matrix a12 * a23) + (matrix a13 * a33) + (matrix a14 * a43)). result a14: ((matrix a11 * a14) + (matrix a12 * a24) + (matrix a13 * a34) + (matrix a14 * a44)). result a21: ((matrix a21 * a11) + (matrix a22 * a21) + (matrix a23 * a31) + (matrix a24 * a41)). result a22: ((matrix a21 * a12) + (matrix a22 * a22) + (matrix a23 * a32) + (matrix a24 * a42)). result a23: ((matrix a21 * a13) + (matrix a22 * a23) + (matrix a23 * a33) + (matrix a24 * a43)). result a24: ((matrix a21 * a14) + (matrix a22 * a24) + (matrix a23 * a34) + (matrix a24 * a44)). result a31: ((matrix a31 * a11) + (matrix a32 * a21) + (matrix a33 * a31) + (matrix a34 * a41)). result a32: ((matrix a31 * a12) + (matrix a32 * a22) + (matrix a33 * a32) + (matrix a34 * a42)). result a33: ((matrix a31 * a13) + (matrix a32 * a23) + (matrix a33 * a33) + (matrix a34 * a43)). result a34: ((matrix a31 * a14) + (matrix a32 * a24) + (matrix a33 * a34) + (matrix a34 * a44)). result a41: ((matrix a41 * a11) + (matrix a42 * a21) + (matrix a43 * a31) + (matrix a44 * a41)). result a42: ((matrix a41 * a12) + (matrix a42 * a22) + (matrix a43 * a32) + (matrix a44 * a42)). result a43: ((matrix a41 * a13) + (matrix a42 * a23) + (matrix a43 * a33) + (matrix a44 * a43)). result a44: ((matrix a41 * a14) + (matrix a42 * a24) + (matrix a43 * a34) + (matrix a44 * a44)). ^result! productFromVector3: aVector3 "Multiply aVector (temporarily converted to 4D) with the receiver" | x y z rx ry rz rw | x := aVector3 x. y := aVector3 y. z := aVector3 z. rx := (x * a11) + (y * a21) + (z * a31) + a41. ry := (x * a12) + (y * a22) + (z * a32) + a42. rz := (x * a13) + (y * a23) + (z * a33) + a43. rw := (x * a14) + (y * a24) + (z * a34) + a44. ^Vector3 x:(rx/rw) y: (ry/rw) z: (rz/rw)! productFromVector4: aVector4 "Multiply aVector with the receiver" | x y z w rx ry rz rw | x := aVector4 x. y := aVector4 y. z := aVector4 z. w := aVector4 w. rx := (x * a11) + (y * a21) + (z * a31) + (w * a41). ry := (x * a12) + (y * a22) + (z * a32) + (w * a42). rz := (x * a13) + (y * a23) + (z * a33) + (w * a43). rw := (x * a14) + (y * a24) + (z * a34) + (w * a44). ^Vector4 x:rx y: ry z: rz w: rw! subtractMatrix4x4: aMatrix4x4 "Subtract aMatrix4x4 from the receiver" | result | result := Matrix4x4 new. result a11: a11 - aMatrix4x4 a11. result a12: a12 - aMatrix4x4 a12. result a13: a13 - aMatrix4x4 a13. result a14: a14 - aMatrix4x4 a14. result a21: a21 - aMatrix4x4 a21. result a22: a22 - aMatrix4x4 a22. result a23: a23 - aMatrix4x4 a23. result a24: a24 - aMatrix4x4 a24. result a31: a31 - aMatrix4x4 a31. result a32: a32 - aMatrix4x4 a32. result a33: a33 - aMatrix4x4 a33. result a34: a34 - aMatrix4x4 a34. result a41: a41 - aMatrix4x4 a41. result a42: a42 - aMatrix4x4 a42. result a43: a43 - aMatrix4x4 a43. result a44: a44 - aMatrix4x4 a44. ^result! ! !Matrix4x4 methodsFor: 'printing'! printOn: aStream "Print the receiver on aStream" 1 to: 4 do:[:r| 1 to: 4 do:[:c| (self at: r at: c) printOn: aStream. aStream nextPut: Character space]. (r < 4) ifTrue:[aStream nextPut: Character cr]].! storeOn: aStream "Store the receiver on aStream" aStream nextPut:$(. aStream store: self class. aStream nextPutAll: ' new'. self getSelectorArray with: self putSelectorArray do:[:get :put| aStream space; nextPutAll: put; space; store: (self perform: get); nextPut:$;]. aStream nextPutAll: 'yourself'. aStream nextPut: $).! ! !Matrix4x4 methodsFor: 'private'! copyValuesFrom: aMatrix4x4 "Copy the fields of aMatrix4x4 into the receiver" a11 := aMatrix4x4 a11. a12 := aMatrix4x4 a12. a13 := aMatrix4x4 a13. a14 := aMatrix4x4 a14. a21 := aMatrix4x4 a21. a22 := aMatrix4x4 a22. a23 := aMatrix4x4 a23. a24 := aMatrix4x4 a24. a31 := aMatrix4x4 a31. a32 := aMatrix4x4 a32. a33 := aMatrix4x4 a33. a34 := aMatrix4x4 a34. a41 := aMatrix4x4 a41. a42 := aMatrix4x4 a42. a43 := aMatrix4x4 a43. a44 := aMatrix4x4 a44. ^self! do: aBlock "Copy the fields of aMatrix4x4 into the receiver" aBlock value: a11. aBlock value: a12. aBlock value: a13. aBlock value: a14. aBlock value: a21. aBlock value: a22. aBlock value: a23. aBlock value: a24. aBlock value: a31. aBlock value: a32. aBlock value: a33. aBlock value: a34. aBlock value: a41. aBlock value: a42. aBlock value: a43. aBlock value: a44. ^self! getSelectorArray "Return an array of selectors for element read access in row major form" ^#(a11 a21 a31 a41 a12 a22 a32 a42 a13 a23 a33 a43 a14 a24 a34 a44)! putSelectorArray "Return an array of selectors for element write access in row major form" ^#(a11: a21: a31: a41: a12: a22: a32: a42: a13: a23: a33: a43: a14: a24: a34: a44:)! with: aMatrix4x4 do: aBlock "Copy the fields of aMatrix4x4 into the receiver" aBlock value: a11 value: aMatrix4x4 a11. aBlock value: a12 value: aMatrix4x4 a12. aBlock value: a13 value: aMatrix4x4 a13. aBlock value: a14 value: aMatrix4x4 a14. aBlock value: a21 value: aMatrix4x4 a21. aBlock value: a22 value: aMatrix4x4 a22. aBlock value: a23 value: aMatrix4x4 a23. aBlock value: a24 value: aMatrix4x4 a24. aBlock value: a31 value: aMatrix4x4 a31. aBlock value: a32 value: aMatrix4x4 a32. aBlock value: a33 value: aMatrix4x4 a33. aBlock value: a34 value: aMatrix4x4 a34. aBlock value: a41 value: aMatrix4x4 a41. aBlock value: a42 value: aMatrix4x4 a42. aBlock value: a43 value: aMatrix4x4 a43. aBlock value: a44 value: aMatrix4x4 a44. ^self! ! !Matrix4x4 methodsFor: 'solving'! inplaceDecomposeLU "Decompose the receiver in place by using gaussian elimination w/o pivot search" | x | 1 to: 4 do:[:j| "i-th equation (row)" j+1 to: 4 do:[:i| x := (self at: i at: j) / (self at: j at: j). j to: 4 do:[:k| self at: i at: k put: (self at: i at: k) - ((self at: j at: k) * x)]. self at: i at: j put: x]]. ! inplaceHouseHolderTransform: aVector "Solve the linear equation self * aVector = x by using HouseHolder's transformation. Note: This scheme is numerically better than using gaussian elimination even though it takes somewhat longer" | d x sigma beta sum s| x := Array with: aVector x with: aVector y with: aVector z with: aVector w. d := Array new: 4. 1 to: 4 do:[:j| sigma := 0.0. j to: 4 do:[:i| sigma := sigma + ((self at: i at: j) squared)]. sigma isZero ifTrue:[^nil]. "matrix is singular" ((self at: j at: j) < 0.0) ifTrue:[ s:= d at: j put: (sigma sqrt)] ifFalse:[ s:= d at: j put: (sigma sqrt negated)]. beta := 1.0 / ( s * (self at: j at: j) - sigma). self at: j at: j put: ((self at: j at: j) - s). "update remaining columns" j+1 to: 4 do:[:k| sum := 0.0. j to: 4 do:[:i| sum := sum + ((self at: i at: j) * (self at: i at: k))]. sum := sum * beta. j to: 4 do:[:i| self at: i at: k put: ((self at: i at: k) + ((self at: i at: j) * sum))]]. "update vector" sum := nil. j to: 4 do:[:i| sum := sum isNil ifTrue:[(x at: i) * (self at: i at: j)] ifFalse:[sum + ((x at: i) * (self at: i at: j))]]. sum := sum * beta. j to: 4 do:[:i| x at: i put:((x at: i) + (sum * (self at: i at: j)))]. ]. "Now calculate result" 4 to: 1 by: -1 do:[:i| i+1 to: 4 do:[:j| x at: i put: ((x at: i) - ((x at: j) * (self at: i at: j))) ]. x at: i put: ((x at: i) / (d at: i))]. ^Vector4 x: (x at: 1) y: (x at: 2) z: (x at: 3) w: (x at: 4) ! inplaceHouseHolderTransformUnrolled: aVector "Unrolled version" | d1 d2 d3 d4 x1 x2 x3 x4 sigma beta sum s| x1 := aVector x. x2 := aVector y. x3 := aVector z. x4 := aVector w. "first column" sigma := (a11 * a11) + (a21 * a21) + (a31 * a31) + (a41 * a41). sigma isZero ifTrue:[^nil]. "matrix singular" d1 := s := (a11 < 0.0) ifTrue:[ sigma sqrt ] ifFalse:[ sigma sqrt negated]. beta := 1.0 / ( (s * a11) - sigma). a11 := a11 - s. "update remaining columns and vector" sum := ((a11 * a12) + (a21 * a22) + (a31 * a32) + (a41 * a42)) * beta. a12 := a12 + (a11 * sum). a22 := a22 + (a21 * sum). a32 := a32 + (a31 * sum). a42 := a42 + (a41 * sum). sum := ((a11 * a13) + (a21 * a23) + (a31 * a33) + (a41 * a43)) * beta. a13 := a13 + (a11 * sum). a23 := a23 + (a21 * sum). a33 := a33 + (a31 * sum). a43 := a43 + (a41 * sum). sum := ((a11 * a14) + (a21 * a24) + (a31 * a34) + (a41 * a44)) * beta. a14 := a14 + (a11 * sum). a24 := a24 + (a21 * sum). a34 := a34 + (a31 * sum). a44 := a44 + (a41 * sum). sum := ((a11 * x1) + (a21 * x2) + (a31 * x3) + (a41 * x4)) * beta. x1 := x1 + (a11 * sum). x2 := x2 + (a21 * sum). x3 := x3 + (a31 * sum). x4 := x4 + (a41 * sum). "second column" sigma := (a22 * a22) + (a32 * a32) + (a42 * a42). sigma isZero ifTrue:[^nil]. "matrix singular" d2 := s := (a22 < 0.0) ifTrue:[ sigma sqrt ] ifFalse:[ sigma sqrt negated]. beta := 1.0 / ( (s * a22) - sigma). a22 := a22 - s. "update remaining columns and vector" sum := ((a22 * a23) + (a32 * a33) + (a42 * a43)) * beta. a23 := a23 + (a22 * sum). a33 := a33 + (a32 * sum). a43 := a43 + (a42 * sum). sum := ((a22 * a24) + (a32 * a34) + (a42 * a44)) * beta. a24 := a24 + (a22 * sum). a34 := a34 + (a32 * sum). a44 := a44 + (a42 * sum). sum := ((a22 * x2) + (a32 * x3) + (a42 * x4)) * beta. x2 := x2 + (a22 * sum). x3 := x3 + (a32 * sum). x4 := x4 + (a42 * sum). "third column" sigma := (a33 * a33) + (a43 * a43). sigma isZero ifTrue:[^nil]. "matrix singular" d3 := s := (a33 < 0.0) ifTrue:[ sigma sqrt ] ifFalse:[ sigma sqrt negated]. beta := 1.0 / ( (s * a33) - sigma). a33 := a33 - s. "update remaining columns and vector" sum := ((a33 * a34) + (a43 * a44)) * beta. a34 := a34 + (a33 * sum). a44 := a44 + (a43 * sum). sum := ((a33 * x3) + (a43 * x4)) * beta. x3 := x3 + (a33 * sum). x4 := x4 + (a43 * sum). "fourth column" sigma := (a44 * a44). sigma isZero ifTrue:[^nil]. "matrix singular" d4 := s := (a44 < 0.0) ifTrue:[ sigma sqrt ] ifFalse:[ sigma sqrt negated]. beta := 1.0 / ( (s * a44) - sigma). a44 := a44 - s. "update remaining columns and vector" sum := (a44 * x4) * beta. x4 := x4 + (a44 * sum). "Now calculate result" x4 := x4 / d4. x3 := (x3 - (a34 * x4)) / d3. x2 := (x2 - ((a23 * x3) + (a24 * x4))) / d2. x1 := (x1 - ((a12 * x2) + (a13 * x3) + (a14 * x4))) / d1. ^Vector4 x: x1 y: x2 z: x3 w: x4! solve: aVector ^self shallowCopy inplaceHouseHolderTransformUnrolled: aVector "or: ^self shallowCopy inplaceDecomposeLU solveLU: aVector or: ^self shallowCopy inplaceHouseHolderTransform: aVector "! solveLU: aVector "Given a decomposed matrix using gaussian elimination solve the linear equations." | x v | v := Array with: aVector x with: aVector y with: aVector z with: aVector w. "L first" 1 to: 4 do:[:i| "Top to bottom" x := 0.0. 1 to: i-1 do:[:j| "From left to right w/o diagonal element" x := x + ((v at: j) * (self at: i at: j))]. "No need to divide by the diagonal element - this is always 1.0 in L" v at: i put: (v at: i) - x]. "Now U" 4 to: 1 by: -1 do:[:i| "Bottom to top" x := 0.0. 4 to: i+1 by: -1 do:[:j| "From right to left w/o diagonal element" x := x + ((v at: j) * (self at: i at: j))]. "Divide by diagonal element" v at: i put: (v at: i) - x / (self at: i at: i)]. ^Vector4 x: (v at: 1) y: (v at: 2) z: (v at: 3) w: (v at: 4) ! ! !Matrix4x4 methodsFor: 'comparing'! = anotherMatrix ^anotherMatrix notNil and:[(self - anotherMatrix) isZero]! hash "Hash is re-implemented because = is re-implemented" ^a43 hash! squaredErrorDistanceTo: anotherMatrix | result temp | result := self - anotherMatrix. temp := 0. 1 to: 4 do: [:i | 1 to: 4 do: [:j| temp := temp + ((result at: i at: j) squared)]]. ^temp sqrt.! ! !Matrix4x4 methodsFor: 'testing'! isZero 1 to: 4 do: [:i | 1 to: 4 do: [:j | (self at: i at: j) = 0.0 ifFalse: [^false]]]. ^true! ! !Matrix4x4 methodsFor: 'converting'! asMatrix ^self! ! !Matrix4x4 class methodsFor: 'constants'! betaSplineBias: beta1 tension: beta2 ^self new betaSplineBias: beta1 tension: beta2! bezier ^self new bezier! bSpline ^self new bSpline! cardinal ^self new cardinal! catmull ^self new catmull! identity ^self new identity! lagrange ^self new lagrange! polyline ^self new polyline! zero ^self new zero! ! !Matrix4x4 class methodsFor: 'instance creation'! withRotation: anAngle around: aVector ^self new identity rotation: anAngle around: aVector! withRotation: anAngle aroundX: xValue y: yValue z: zValue ^self withRotation: anAngle around: (Vector3 with: xValue with: yValue with: zValue)! withRotationAroundX: anAngle ^self new identity rotationAroundX: anAngle! withRotationAroundY: anAngle ^self new identity rotationAroundY: anAngle! withRotationAroundZ: anAngle ^self new identity rotationAroundZ: anAngle! withScaling: aVector ^self withScalingX: aVector x y: aVector y z: aVector z! withScalingX: xValue y: yValue z: zValue ^self new identity scalingX: xValue y: yValue z: zValue! withTranslation: aVector ^self withTranslationX: aVector x y: aVector y z: aVector z! withTranslationX: xValue y: yValue z: zValue ^self new identity translationX: xValue y: yValue z:zValue! ! !Number methodsFor: 'testing'! isZero ^self = 0! ! !Number methodsFor: 'double dispatching'! productFromMatrix4x4: aMatrix ^aMatrix multiplyNumber: self! productFromVector3: aVector ^aVector multiplyNumber: self! productFromVector4: aVector ^aVector multiplyNumber: self! quotientFromVector3: aVector ^aVector divideNumber: self! quotientFromVector4: aVector ^aVector divideNumber: self! ! !Float methodsFor: 'testing'! isZero ^self = 0.0! ! !Point methodsFor: 'accessing'! z ^0! z: aNumber ^self shouldNotImplement! ! !Point methodsFor: 'converting'! @ aNumber ^Vector3 x: x y: y z: aNumber! asVector3 ^Vector3 x: x y: y z: 0.0! asVector3: zValue ^Vector3 x: x y: y z: zValue! asVector4 ^Vector4 x: x y: y z: 0.0 w: 1.0! ! !Rectangle methodsFor: 'accessing'! centerNotRounded ^(origin + corner) / 2! ! Smalltalk renameClassNamed: #Quaternion as: #Rotation! Rotation comment: '(c) 1994-1997 Andreas Raab and Stefan Schlechtweg, University of Magdeburg, Germany I represent general 3d rotations by using Unit-Quaternions. Unit-Quaternions are one of the best available representation for rotations in computer graphics because they provide an easy way of doing arithmetic with them and also because they allow us to use spherical linear interpolation (so-called "slerps") of rotations. Instance Variables: a the real part of the quaternion b the first imaginary part of the quaternion c the second imaginary part of the quaternion d the third imaginary part of the quaternion '! !Rotation reorganize! ('initialize-release' a:b:c:d: angle:axis: from:to: identity radiansAngle:axis: x:y:z:a:) ('accessing' a angle axis b c d) ('arithmetic' * dot: negated normalize) ('converting' asMatrix normalized) ('interpolating' slerpTo:at: slerpTo:at:extraSpins:) ('printing' printOn:) ('private' bcd matrixClass) ! !Rotation methodsFor: 'initialize-release'! a: aValue b: bValue c: cValue d: dValue a := aValue. b := bValue. c := cValue. d := dValue. (a < 0.0) ifTrue:[ a := a negated. b := b negated. c := c negated. d := d negated]. self normalize.! angle: anAngle axis: aVector3 self radiansAngle: anAngle degreesToRadians axis: aVector3 ! from: startVector to: endVector "Create a rotation from startVector to endVector" | axis cos sin | axis := startVector cross: endVector. cos := (startVector dot: endVector) arcCos. sin := axis length. axis safelyNormalize. self a: cos b: axis x * sin c: axis y * sin d: axis z * sin. ! identity a := 1.0. b := c := d := 0.0.! radiansAngle: anAngle axis: aVector3 | angle sin cos | angle := anAngle / 2.0. cos := angle cos. sin := angle sin. self a: cos b: aVector3 x * sin c: aVector3 y * sin d: aVector3 z * sin.! x: xValue y: yValue z: zValue a: anAngle | angle sin cos | angle := (anAngle degreesToRadians) / 2.0. cos := angle cos. sin := angle sin. self a: cos b: xValue * sin c: yValue * sin d: zValue * sin! ! !Rotation methodsFor: 'accessing'! a ^a! angle ^(a arcCos * 2.0 radiansToDegrees)! axis | sinAngle | sinAngle := a arcCos sin. sinAngle isZero ifTrue:[^Vector3 zero]. ^Vector3 x: (b / sinAngle) y: (c / sinAngle) z: (d / sinAngle)! b ^b! c ^c! d ^d! ! !Rotation methodsFor: 'arithmetic'! * aRotation "Multiplying two rotations is the same as concatenating the two rotations." | v1 v2 v3 vv | v1 := self bcd * aRotation a. v2 := aRotation bcd * self a. v3 := aRotation bcd cross: self bcd. vv := v1 + v2 + v3. ^Rotation a: (self a * aRotation a) - (self bcd dot: aRotation bcd) b: vv x c: vv y d: vv z! dot: aRotation ^(a * aRotation a) + (b * aRotation b) + (c * aRotation c) + (d * aRotation d)! negated "Negating a quaternion is the same as reversing the angle of rotation" ^Rotation a: a negated b: b c: c d: d! normalize "Normalize the receiver. Note that the actual angle (a) determining the amount of rotation is fixed, since we do not want to modify angles. This leads to: a^2 + b^2 + c^2 + d^2 = 1. b^2 + c^2 + d^2 = 1 - a^2. Note also that the angle (a) can not exceed 1.0 (due its creation by cosine) and if it is 1.0 we have exactly the unit quaternion ( 1, [ 0, 0, 0]). " | oneMinusASquared length | oneMinusASquared := 1.0 - (a * a). (oneMinusASquared < 1.0e-10) ifTrue:[^self identity]. length := ((b squared + c squared + d squared) / oneMinusASquared) sqrt. b := b / length. c := c / length. d := d / length. ! ! !Rotation methodsFor: 'converting'! asMatrix "Given a quaternion q = (a, [ b, c , d]) the rotation matrix can be calculated as | 1 - 2(cc+dd), 2(bc-da), 2(db+ca) | m = | 2(bc+da), 1 - 2(bb+dd), 2(cd-ba) | | 2(db-ca), 2(cd+ba), 1 - 2(bb+cc) | " | m bb cc dd bc cd db ba ca da | bb := (b * b). cc := (c * c). dd := (d * d). bc := (b * c). cd := (c * d). db := (d * b). ba := (b * a). ca := (c * a). da := (d * a). m := self matrixClass identity. m a11: 1.0 - (cc + dd * 2.0);a12: (bc - da * 2.0); a13: (db + ca * 2.0); a21: (bc + da * 2.0); a22: 1.0 - (bb + dd * 2.0);a23: (cd - ba * 2.0); a31: (db - ca * 2.0); a32: (cd + ba * 2.0); a33: 1.0 - (bb + cc * 2.0). ^m ! normalized ^self copy normalize! ! !Rotation methodsFor: 'interpolating'! slerpTo: aQuaternion at: t "Spherical linear interpolation (slerp) from the receiver to aQuaternion" ^self slerpTo: aQuaternion at: t extraSpins: 0! slerpTo: aRotation at: t extraSpins: spin "Sperical Linear Interpolation (slerp). Calculate the new quaternion when applying slerp from the receiver (t = 0.0) to aRotation (t = 1.0). spin indicates the number of extra rotations to be added. The code shown below is from Graphics Gems III" | cosT alpha beta flip theta phi sinT | alpha := t. flip := false. "calculate the cosine of the two quaternions on the 4d sphere" cosT := self dot: aRotation. "if aQuaternion is on the opposite hemisphere reverse the direction (note that in quaternion space two points describe the same rotation)" cosT < 0.0 ifTrue:[ flip := true. cosT := cosT negated]. "If the aQuaternion is nearly the same as I am use linear interpolation" cosT > 0.99999 ifTrue:[ "Linear Interpolation" beta := 1.0d - alpha ] ifFalse:[ "Spherical Interpolation" theta := cosT arcCos. phi := (spin * Float pi) + theta. sinT := theta sin. beta := (theta - (alpha * phi)) sin / sinT. alpha := (alpha * phi) sin / sinT]. flip ifTrue:[alpha := alpha negated]. ^Rotation a: (alpha * aRotation a) + (beta * a) b: (alpha * aRotation b) + (beta * b) c: (alpha * aRotation c) + (beta * c) d: (alpha * aRotation d) + (beta * d) ! ! !Rotation methodsFor: 'printing'! printOn: aStream aStream nextPutAll: self class name; nextPut:$(; print: self angle; nextPut: Character space; print: self axis; nextPut:$).! ! !Rotation methodsFor: 'private'! bcd ^Vector3 x: b y: c z: d! matrixClass ^Matrix4x4! ! !Rotation class methodsFor: 'instance creation'! a: aValue b: bValue c: cValue d: dValue ^self new a: aValue b: bValue c: cValue d: dValue! angle: anAngle axis: aVector3 ^self new angle: anAngle axis: aVector3! axis: aVector3 angle: anAngle ^self angle: anAngle axis: aVector3! from: startVector to: endVector ^self new from: startVector to: endVector! identity ^self new identity! radiansAngle: anAngle axis: aVector3 ^self new radiansAngle: anAngle axis: aVector3! x: xValue y: yValue z: zValue a: anAngle ^self new x: xValue y: yValue z: zValue a: anAngle! ! Vector3 comment: '(c) 1994-1997 Andreas Raab and Stefan Schlechtweg, University of Magdeburg, Germany I represent a 3d vector'! !Vector3 reorganize! ('initialize-release' x:y:z: x:y:z:w:) ('accessing' w x x: y y: z z:) ('arithmetic' * + += - -= / cross: dot: max: min: rounded truncated truncateTo:) ('vector functions' length negate negated normalized safelyNormalize) ('double dispatching' divideNumber: multiplyNumber: productFromMatrix4x4: productFromVector3: quotientFromVector3:) ('testing' isZero) ('converting' @ asColor asPoint asVector3 asVector3: asVector4) ('comparing' = hash) ('printing' printOn: storeOn:) ! !Vector3 methodsFor: 'initialize-release'! x: aX y: aY z: aZ x := aX. y := aY. z := aZ. ^self! x: aX y: aY z: aZ w: aW x := (aX / aW). y := (aY / aW). z := (aZ / aW). ^self! ! !Vector3 methodsFor: 'accessing'! w ^1.0! x ^x! x: aValue x := aValue! y ^y! y: aValue y := aValue! z ^z! z: aValue z := aValue! ! !Vector3 methodsFor: 'arithmetic'! * anObject ^anObject productFromVector3: self! + aVector3 "add aVector3 to the receiver." ^Vector3 x: self x + aVector3 x y: self y + aVector3 y z: self z + aVector3 z! += aVector3 "add aVector3 to the receiver." x := x + aVector3 x. y := y + aVector3 y. z := z + aVector3 z. ! - aVector3 "subtract aVector3 from the receiver." ^Vector3 x: self x - aVector3 x y: self y - aVector3 y z: self z - aVector3 z! -= aVector3 "subtract aVector3 from the receiver." x := x - aVector3 x. y := y - aVector3 y. z := z - aVector3 z.! / anObject ^anObject quotientFromVector3: self! cross: aVector "calculate the cross product from the receiver with aVector" ^Vector3 x: y * aVector z - (aVector y * z) y: z * aVector x - (aVector z * x) z: x * aVector y - (aVector x * y)! dot: aVector "return the dot product from the receiver and aVector" ^x * aVector x + (y * aVector y) + (z * aVector z)! max: aVector3 "get a new vector whose components are the maximal component of the receiver and aVector3" ^Vector3 x: (self x max: aVector3 x) y: (self y max: aVector3 y) z: (self z max: aVector3 z)! min: aVector3 "get a new vector whose components are the minimal component of the receiver and aVector3" ^Vector3 x: (self x min: aVector3 x) y: (self y min: aVector3 y) z: (self z min: aVector3 z)! rounded ^Vector3 x: x rounded y: y rounded z: z rounded! truncated ^Vector3 x: x truncated y: y truncated z: z truncated! truncateTo: aNumber ^(self / aNumber) truncated * aNumber! ! !Vector3 methodsFor: 'vector functions'! length "return the vector's legth. Also possible as (self dot: self) sqrt" ^((x * x) + (y * y) + (z * z)) sqrt! negate "negate the receiver in-place" x := x negated. y := y negated. z := z negated! negated "return the negated receiver that means Vector3 zero - self" ^Vector3 x: x negated y: y negated z: z negated! normalized "returns the the vector of length 1 which is constructed out of the receiver" ^self / self length! safelyNormalize "Safely normalize the receiver (i.e. avoid dividing by zero)" | length | length := self length. length = 0.0 ifFalse: [ x := x / length. y := y / length. z := z / length. ].! ! !Vector3 methodsFor: 'double dispatching'! divideNumber: aNumber ^Vector3 x: x / aNumber y: y / aNumber z: z / aNumber! multiplyNumber: aNumber ^Vector3 x: x * aNumber y: y * aNumber z: z * aNumber! productFromMatrix4x4: aMatrix "perform the multiplication aMatrix * self where aMatrix is a 4x4 Matrix. Implicitely we convert the Vector to 4D, multiply and convert back. We assume the fourth coordinate to be 1 at the beginning and at the end" | newX newY newZ w | newX := aMatrix a11 * x + (aMatrix a12 * y) + (aMatrix a13 * z) + aMatrix a14. newY := aMatrix a21 * x + (aMatrix a22 * y) + (aMatrix a23 * z) + aMatrix a24. newZ := aMatrix a31 * x + (aMatrix a32 * y) + (aMatrix a33 * z) + aMatrix a34. w := aMatrix a41 * x + (aMatrix a42 * y) + (aMatrix a43 * z) + aMatrix a44. ^Vector3 x: newX / w y: newY / w z: newZ / w ! productFromVector3: v3 "Behave as Point does" ^Vector3 x: x * v3 x y: y * v3 y z: z * v3 z! quotientFromVector3: aVector3 "Behave as Point does" ^Vector3 x: aVector3 x / x y: aVector3 y / y z: aVector3 z / z! ! !Vector3 methodsFor: 'testing'! isZero ^x = 0.0 and: [y = 0.0 and: [z = 0.0]]! ! !Vector3 methodsFor: 'converting'! @ aNumber ^Vector4 x: x y: y z: z w: aNumber! asColor ^Color red: (x min: 1.0 max: 0.0) green: (y min: 1.0 max: 0.0) blue: (z min: 1.0 max: 0.0)! asPoint ^(self x) @ (self y)! asVector3 ^self! asVector3: zValue ^self! asVector4 ^Vector4 x: (self x) y:(self y) z: (self z) w: 1.0! ! !Vector3 methodsFor: 'comparing'! = aVector ^aVector notNil and:[ (x = aVector x) and:[ (y = aVector y) and:[ (z = aVector z) and: [ 1.0 = aVector w]]]]! hash "Hash is re-implemented because = is re-implemented" ^(x + y + z + 1.0) hash ! ! !Vector3 methodsFor: 'printing'! printOn: aStream aStream nextPut:$(; print: x; nextPut:$@; print: y; nextPut:$@; print: z; nextPut:$)! storeOn: aStream aStream nextPut:$(; store: x; nextPut:$@; store: y; nextPut:$@; store: z; nextPut:$)! ! !Vector3 class methodsFor: 'class initialization'! initialize "Vector3 initialize" Black := Color black asVector3. Blue := Color blue asVector3. Cyan := Color cyan asVector3. Green := Color green asVector3. Magenta := Color magenta asVector3. Orange := Color orange asVector3. Red := Color red asVector3. White := Color white asVector3. Yellow := Color yellow asVector3. Gray := Color gray asVector3.! ! !Vector3 class methodsFor: 'constants'! black ^Black! blue ^Blue! cyan ^Cyan! gray ^Gray! green ^Green! magenta ^Magenta! orange ^Orange! red ^Red! white ^White! yellow ^Yellow! zero "creates a zero vector" ^self new x: 0.0 y: 0.0 z: 0.0! ! !Vector3 class methodsFor: 'instance creation'! with: anArray ^self new x: (anArray at: 1) y: (anArray at: 2) z: (anArray at: 3)! with: value1 with: value2 with: value3 ^self new x: value1 y: value2 z: value3! x: anX y: aY z: aZ ^self new x: anX y: aY z: aZ! ! Vector4 comment: '(c) 1994-1997 Andreas Raab and Stefan Schlechtweg, University of Magdeburg, Germany I represent a 3d vector in homogenous coordinates'! !Vector4 reorganize! ('initialize-release' x:y:z: x:y:z:w:) ('accessing' w w: x x: y y: z z:) ('arithmetic' * + += - -= / dot:) ('vector functions' length negate negated normalized) ('double dispatching' divideNumber: multiplyNumber: productFromMatrix4x4: productFromVector4: quotientFromVector4:) ('testing' isZero) ('converting' asPoint asVector3 asVector4) ('comparing' = hash) ('printing' printOn: storeOn:) ! !Vector4 methodsFor: 'initialize-release'! x: aX y: aY z: aZ x := aX. y := aY. z := aZ. w := 1.0. ^self! x: aX y: aY z: aZ w: aW x := aX. y := aY. z := aZ. w := aW. ^self! ! !Vector4 methodsFor: 'accessing'! w ^w! w: aValue w := aValue! x ^x! x: aValue x := aValue! y ^y! y: aValue y := aValue! z ^z! z: aValue z := aValue! ! !Vector4 methodsFor: 'arithmetic'! * anObject ^anObject productFromVector4: self! + aVector4 "add aVector4 to the receiver." ^Vector4 x: x + aVector4 x y: y + aVector4 y z: z + aVector4 z w: w + aVector4 w! += aVector4 "add aVector3 to the receiver." x := x + aVector4 x. y := y + aVector4 y. z := z + aVector4 z. w := w + aVector4 w.! - aVector4 "subtract aVector4 from the receiver." ^Vector4 x: x - aVector4 x y: y - aVector4 y z: z - aVector4 z w: w - aVector4 w! -= aVector4 "add aVector3 to the receiver." x := x - aVector4 x. y := y - aVector4 y. z := z - aVector4 z. w := w - aVector4 w.! / anObject ^anObject quotientFromVector4: self! dot: aVector "return the dot product from the receiver and aVector" ^x * aVector x + (y * aVector y) + (z * aVector z) + (w * aVector w)! ! !Vector4 methodsFor: 'vector functions'! length "return the vector's legth. Also possible as (self dot: self) sqrt" ^((x * x) + (y * y) + (z * z) + (w * w)) sqrt! negate "negate the receiver in-place" x := x negated. y := y negated. z := z negated. w := w negated! negated "return the negated receiver that means Vector4 zero - self" ^Vector4 x: x negated y: y negated z: z negated w: w negated! normalized "returns the the vector of length 1 which is constructed out of the receiver" ^self / self length! ! !Vector4 methodsFor: 'double dispatching'! divideNumber: aNumber ^Vector4 x: x / aNumber y: y / aNumber z: z / aNumber w: w / aNumber! multiplyNumber: aNumber ^Vector4 x: x * aNumber y: y * aNumber z: z * aNumber w: w * aNumber! productFromMatrix4x4: aMatrix "perform the multiplication aMatrix * self where aMatrix is a 4x4 Matrix " | newX newY newZ newW | newX := aMatrix a11 * x + (aMatrix a12 * y) + (aMatrix a13 * z) + (aMatrix a14 * w). newY := aMatrix a21 * x + (aMatrix a22 * y) + (aMatrix a23 * z) + (aMatrix a24 * w). newZ := aMatrix a31 * x + (aMatrix a32 * y) + (aMatrix a33 * z) + (aMatrix a34 * w). newW := aMatrix a41 * x + (aMatrix a42 * y) + (aMatrix a43 * z) + (aMatrix a44 * w). ^Vector4 x: newX y: newY z: newZ w: newW! productFromVector4: v4 "Behave as Point does" ^Vector4 x: x * v4 x y: y * v4 y z: z * v4 z w: w * v4 w! quotientFromVector4: aVector4 "Behave as Point does" ^Vector4 x: aVector4 x / x y: aVector4 y / y z: aVector4 z / z w: aVector4 w / w! ! !Vector4 methodsFor: 'testing'! isZero ^x = 0.0 and: [y = 0.0 and: [z = 0.0 and: [w = 0.0]]]! ! !Vector4 methodsFor: 'converting'! asPoint ^(self x / self w) @ (self y / self w)! asVector3 ^Vector3 x: (self x / self w) y: (self y / self w) z: (self z / self w)! asVector4 ^self! ! !Vector4 methodsFor: 'comparing'! = aVector ^aVector notNil and:[ (x = aVector x) and:[ (y = aVector y) and:[ (z = aVector z) and: [ w = aVector w]]]]! hash "Hash is re-implemented because = is re-implemented" ^(x + y + z + w) asFloat hash ! ! !Vector4 methodsFor: 'printing'! printOn: aStream aStream nextPut:$(; print: x; nextPut:$@; print: y; nextPut:$@; print: z; nextPut:$@; print: w; nextPut:$) ! storeOn: aStream aStream nextPut:$(; store: x; nextPut:$@; store: y; nextPut:$@; store: z; nextPut:$@; store: w; nextPut:$) ! ! !Vector4 class methodsFor: 'constants'! zero "creates a zero vector" ^self new x: 0.0 y: 0.0 z: 0.0 w: 0.0! ! !Vector4 class methodsFor: 'instance creation'! with: value1 with: value2 with: value3 with: value4 ^self new x: value1 y: value2 z: value3 w: value4! x: anX y: aY z: aZ w: aW ^self new x: anX y: aY z: aZ w: aW! ! Vector3 initialize!