groups.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*
  2. GROUPS
  3. Groups are collections of an arbitrary number of Cubelets.
  4. They have no concept of Cubelet location or orientation
  5. and therefore are not capable of rotation around any axis.
  6. */
  7. export function Group(){
  8. this.cubelets = []
  9. this.add( Array.prototype.slice.call( arguments ))
  10. }
  11. globalThis.setupTasks = globalThis.setupTasks || []
  12. globalThis.setupTasks.push( function(){
  13. globalThis.augment( Group, {
  14. inspect: function( face ){
  15. this.cubelets.forEach( function( cubelet ){
  16. cubelet.inspect( face )
  17. })
  18. return this
  19. },
  20. add: function(){
  21. var
  22. cubeletsToAdd = Array.prototype.slice.call( arguments ),
  23. that = this
  24. cubeletsToAdd.forEach( function( cubelet ){
  25. if( cubelet instanceof Group ) cubelet = cubelet.cubelets
  26. if( cubelet instanceof Array ) that.add.apply( that, cubelet )
  27. else that.cubelets.push( cubelet )
  28. })
  29. return this
  30. },
  31. remove: function( cubeletToRemove ){
  32. if( cubeletToRemove instanceof Group ) cubeletToRemove = cubeletToRemove.cubelets
  33. if( cubeletToRemove instanceof Array ){
  34. var that = this
  35. cubeletToRemove.forEach( function( c ){
  36. that.remove( c )
  37. })
  38. }
  39. for( var i = this.cubelets.length - 1; i >= 0; i -- ){
  40. if( this.cubelets[ i ] === cubeletToRemove )
  41. this.cubelets.splice( i, 1 )
  42. }
  43. return this
  44. },
  45. // Boolean checker.
  46. // Are any Cubelets in this group tweening?
  47. // Engaged on the Z axis? Etc.
  48. isFlagged: function( property ){
  49. var count = 0
  50. this.cubelets.forEach( function( cubelet ){
  51. count += cubelet[ property ] ? 1 : 0
  52. })
  53. return count
  54. },
  55. isTweening: function(){
  56. return this.isFlagged( 'isTweening' )
  57. },
  58. isEngagedX: function(){
  59. return this.isFlagged( 'isEngagedX' )
  60. },
  61. isEngagedY: function(){
  62. return this.isFlagged( 'isEngagedY' )
  63. },
  64. isEngagedZ: function(){
  65. return this.isFlagged( 'isEngagedZ' )
  66. },
  67. isEngaged: function(){
  68. return this.isEngagedX() + this.isEngagedY() + this.isEngagedZ()
  69. },
  70. // Search functions.
  71. // What Cubelets in this Group have a particular color?
  72. // How about all of these three colors?
  73. // And index? address? Solver uses these a lot.
  74. hasProperty: function( property, value ){
  75. var
  76. results = new Group()
  77. this.cubelets.forEach( function( cubelet ){
  78. if( cubelet[ property ] === value ) results.add( cubelet )
  79. })
  80. return results
  81. },
  82. hasId: function( id ){
  83. return this.hasProperty( 'id', id ).cubelets[ 0 ]// expecting a single return!
  84. },
  85. hasAddress: function( address ){
  86. return this.hasProperty( 'address', address ).cubelets[ 0 ]// expecting a single return!
  87. },
  88. hasType: function( type ){
  89. return this.hasProperty( 'type', type )
  90. },
  91. hasColor: function( color ){
  92. var
  93. results = new Group()
  94. this.cubelets.forEach( function( cubelet ){
  95. if( cubelet.hasColor( color )) results.add( cubelet )
  96. })
  97. return results
  98. },
  99. hasColors: function(){// this function implies AND rather than OR, XOR, etc.
  100. var
  101. results = new Group(),
  102. colors = Array.prototype.slice.call( arguments )
  103. this.cubelets.forEach( function( cubelet ){
  104. if( cubelet.hasColors.apply( cubelet, colors )) results.add( cubelet )
  105. })
  106. return results
  107. },
  108. // We needed this business in order to deal with partial rotations.
  109. // A little janky perhaps (grabbing the average, that is)
  110. // but gets the job done and allows us to do getDistanceToPeg() below.
  111. getAverageRotation: function( axis ){
  112. var sum = 0
  113. this.cubelets.forEach( function( cubelet ){
  114. sum += cubelet[ axis.toLowerCase() ]
  115. })
  116. return sum / this.cubelets.length
  117. },
  118. getAverageRotationX: function(){
  119. return this.getAverageRotation( 'x' )
  120. },
  121. getAverageRotationY: function(){
  122. return this.getAverageRotation( 'y' )
  123. },
  124. getAverageRotationZ: function(){
  125. return this.getAverageRotation( 'z' )
  126. },
  127. // What rotation degree are we on right now?
  128. // What direction are we spinning from here? (Clockwise or anti?)
  129. // Let's go in that direction until we hit degree % 90 === 0.
  130. // What's the distance from here to there?
  131. // Not the prettiest code I've ever written... Sorry.
  132. getDistanceToPeg: function( axis ){
  133. var
  134. current = this.getAverageRotation( axis ),
  135. direction = axis.toUpperCase() === axis ? 'clockwise' : 'anticlockwise',
  136. distance = current
  137. .add( 90 )
  138. .divide( 90 )
  139. .roundDown()
  140. .multiply( 90 )
  141. .subtract( current ),
  142. target = current + distance
  143. if( direction === 'anticlockwise' ){
  144. distance -= 90
  145. if( distance === 0 ) distance -= 90
  146. target = current + distance
  147. }
  148. if( Cube.verbosity >= 0.9 ) console.log(
  149. 'Average rotation for this group about the '+ axis.toUpperCase() +' axis:', current,
  150. '\nRotation direction:', direction,
  151. '\nDistance to next peg:', distance,
  152. '\nTarget rotation:', target
  153. )
  154. // We need to return the absolute() value of the distance
  155. // because the vector (direction) of the twist will be taken into account
  156. // by cube.twist() or whatever else is calling this function.
  157. return distance.absolute()
  158. },
  159. // cube.front.isSolved( 'front' )
  160. // cube.front.up.isSolved( 'up' )
  161. isSolved: function( face ){
  162. if( face ){
  163. var
  164. faceColors = {},
  165. numberOfColors = 0
  166. if( face instanceof Direction ) face = face.name
  167. this.cubelets.forEach( function( cubelet ){
  168. var color = cubelet[ face ].color.name
  169. if( faceColors[ color ] === undefined ){
  170. faceColors[ color ] = 1
  171. numberOfColors ++
  172. }
  173. else faceColors[ color ] ++
  174. })
  175. return numberOfColors === 1 ? true : false
  176. }
  177. else {
  178. console.warn( 'A face [String or Direction] argument must be specified when using Group.isSolved().' )
  179. return false
  180. }
  181. },
  182. // Visual switches.
  183. // Take this group and hide all the stickers,
  184. // turn on wireframe mode, etc.
  185. show: function(){
  186. this.cubelets.forEach( function( cubelet ){ cubelet.show() })
  187. return this
  188. },
  189. hide: function(){
  190. this.cubelets.forEach( function( cubelet ){ cubelet.hide() })
  191. return this
  192. },
  193. showPlastics: function(){
  194. this.cubelets.forEach( function( cubelet ){ cubelet.showPlastics() })
  195. return this
  196. },
  197. hidePlastics: function(){
  198. this.cubelets.forEach( function( cubelet ){ cubelet.hidePlastics() })
  199. return this
  200. },
  201. showExtroverts: function(){
  202. this.cubelets.forEach( function( cubelet ){ cubelet.showExtroverts() })
  203. return this
  204. },
  205. hideExtroverts: function(){
  206. this.cubelets.forEach( function( cubelet ){ cubelet.hideExtroverts() })
  207. return this
  208. },
  209. showIntroverts: function(){
  210. this.cubelets.forEach( function( cubelet ){ cubelet.showIntroverts() })
  211. return this
  212. },
  213. hideIntroverts: function(){
  214. this.cubelets.forEach( function( cubelet ){ cubelet.hideIntroverts() })
  215. return this
  216. },
  217. showStickers: function(){
  218. this.cubelets.forEach( function( cubelet ){ cubelet.showStickers() })
  219. return this
  220. },
  221. hideStickers: function(){
  222. this.cubelets.forEach( function( cubelet ){ cubelet.hideStickers() })
  223. return this
  224. },
  225. showWireframes: function(){
  226. this.cubelets.forEach( function( cubelet ){ cubelet.showWireframes() })
  227. return this
  228. },
  229. hideWireframes: function(){
  230. this.cubelets.forEach( function( cubelet ){ cubelet.hideWireframes() })
  231. return this
  232. },
  233. showIds: function(){
  234. this.cubelets.forEach( function( cubelet ){ cubelet.showIds() })
  235. return this
  236. },
  237. hideIds: function(){
  238. this.cubelets.forEach( function( cubelet ){ cubelet.hideIds() })
  239. return this
  240. },
  241. showTexts: function(){
  242. this.cubelets.forEach( function( cubelet ){ cubelet.showTexts() })
  243. return this
  244. },
  245. hideTexts: function(){
  246. this.cubelets.forEach( function( cubelet ){ cubelet.hideTexts() })
  247. return this
  248. },
  249. getOpacity: function(){
  250. var avg = 0
  251. this.cubelets.forEach( function( cubelet ){ avg += cubelet.getOpacity() })
  252. return avg / this.cubelets.length
  253. },
  254. setOpacity: function( opacity, onComplete ){
  255. this.cubelets.forEach( function( cubelet ){ cubelet.setOpacity( opacity, onComplete ) })
  256. return this
  257. },
  258. getRadius: function(){
  259. var avg = 0
  260. this.cubelets.forEach( function( cubelet ){ avg += cubelet.getRadius() })
  261. return avg / this.cubelets.length
  262. },
  263. setRadius: function( radius, onComplete ){
  264. this.cubelets.forEach( function( cubelet ){ cubelet.setRadius( radius, onComplete ) })
  265. return this
  266. }
  267. })
  268. })