PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/class_artists_similar.php

https://github.com/opensciencefund/gazelle
PHP | 372 lines | 275 code | 68 blank | 29 comment | 37 complexity | 56e1dc71e7e11ad6bb811073a3b3b648 MD5 | raw file
  1. <?
  2. class ARTISTS_SIMILAR extends ARTIST{
  3. var $Artists = array();
  4. var $TotalScore = 0;
  5. var $xValues = array(WIDTH=>1);
  6. var $yValues = array(HEIGHT=>1);
  7. var $LargestDecimal = 0;
  8. var $LowestDecimal = 1;
  9. function dump_data(){
  10. return serialize(array(time(), $this->Name, $this->x, $this->y, serialize($this->Artists), serialize($this->Similar)));
  11. }
  12. function load_data($Data){
  13. list($LastUpdated, $this->Name, $this->x, $this->y, $this->Artists, $this->Similar) = unserialize($Data);
  14. $this->Artists = unserialize($this->Artists);
  15. $this->Similar = unserialize($this->Similar);
  16. }
  17. function set_up(){
  18. $this->x = ceil(WIDTH/2);
  19. $this->y = ceil(HEIGHT/2);
  20. $this->xValues[$this->x] = $this->ID;
  21. $this->yValues[$this->y] = $this->ID;
  22. global $DB;
  23. // Get artists that are directly similar to the artist
  24. $ArtistIDs = array();
  25. $DB->query("
  26. SELECT
  27. s2.ArtistID,
  28. ag.Name,
  29. ass.Score
  30. FROM artists_similar AS s1
  31. JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID
  32. JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID
  33. JOIN artists_group AS ag ON ag.ArtistID=s2.ArtistID
  34. WHERE s1.ArtistID=".$this->ID."
  35. ORDER BY ass.Score DESC
  36. LIMIT 14");
  37. if($DB->record_count() == 0){
  38. return;
  39. }
  40. // Build into array. Each artist is its own object in $this->Artists
  41. while(list($ArtistID, $Name, $Score) = $DB->next_record()){
  42. if($Score<0){
  43. continue;
  44. }
  45. $this->Artists[$ArtistID] = new ARTIST($ArtistID, $Name);
  46. $this->Similar[$ArtistID] = array('ID'=>$ArtistID,'Score'=>$Score);
  47. $this->TotalScore+=$Score;
  48. $ArtistIDs[]=$ArtistID;
  49. }
  50. // Get similarities between artists on the map
  51. $DB->query("SELECT
  52. s1.ArtistID,
  53. s2.ArtistID
  54. FROM artists_similar AS s1
  55. JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID
  56. JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID
  57. JOIN artists_group AS a ON a.ArtistID=s2.ArtistID
  58. WHERE s1.ArtistID IN(".implode(',',$ArtistIDs).")
  59. AND s2.ArtistID IN(".implode(',',$ArtistIDs).")
  60. ");
  61. // Build into array
  62. while(list($Artist1ID, $Artist2ID) = $DB->next_record()){
  63. $this->Artists[$Artist1ID]->Similar[$Artist2ID] = array('ID'=>$Artist2ID);
  64. }
  65. // Calculate decimal point scores between artists
  66. foreach($this->Similar as $SimilarArtist) {
  67. list($ArtistID, $Similar) = array_values($SimilarArtist);
  68. $this->Similar[$ArtistID]['Decimal'] = $this->similarity($Similar['Score'], $this->TotalScore);
  69. if($this->Similar[$ArtistID]['Decimal'] < $this->LowestDecimal){
  70. $this->LowestDecimal = $this->Similar[$ArtistID]['Decimal'];
  71. }
  72. if($this->Similar[$ArtistID]['Decimal'] > $this->LargestDecimal){
  73. $this->LargestDecimal = $this->Similar[$ArtistID]['Decimal'];
  74. }
  75. }
  76. reset($this->Artists);
  77. }
  78. function set_positions(){
  79. $xValues = array(); // Possible x values
  80. $Root = ceil(WIDTH/4); // Half-way into half of the image
  81. $Offset = 4; // Distance from the root (a quarter of the way into the image) to the x value
  82. // The number of artists placed in the top or the bottom
  83. $NumTop = 0;
  84. $NumBottom = 0;
  85. // The number of artists placed in the left or the right
  86. $NumLeft = 0;
  87. $NumRight = 0;
  88. $Multiplier = 0;
  89. // Build up an impressive list of possible x values
  90. // We later iterate through these, and pick out the ones we want
  91. // These x values are all below WIDTH/2 (all on the left)
  92. // The script later chooses which side to put them on
  93. // We create more very low x values because they're more likely to be skipped
  94. for($i = 0; $i<=count($this->Artists)*4; $i++){
  95. if($Offset>=((WIDTH/4))){
  96. $Offset=$Offset%(WIDTH/4);
  97. }
  98. $Plus = $Root+$Offset; // Point on the right of the root
  99. $Minus = abs($Root-$Offset); // Point on the left of the root
  100. $xValues[$Plus]=$Plus;
  101. $xValues[$Minus]=$Minus;
  102. // Throw in an extra x value closer to the edge, because they're more likely to be skipped
  103. if($Minus>30){
  104. // $xValues[$Minus-30]=$Minus-30;
  105. }
  106. $Offset = $Offset+rand(5,20); // Increase offset, and go again
  107. }
  108. foreach($this->Artists as $Artist){
  109. $ArtistID = $Artist->ID;
  110. if($Artist->Displayed == true){
  111. continue;
  112. }
  113. $this->Similar[$ArtistID]['Decimal'] = $this->Similar[$ArtistID]['Decimal'] * (1/($this->LargestDecimal))-0.1;
  114. // Calculate the distance away from the center, based on similarity
  115. $IdealDistance = $this->calculate_distance($this->Similar[$ArtistID]['Decimal'], $this->x, $this->y);
  116. $this->Similar[$ArtistID]['Distance'] = $IdealDistance;
  117. // 1 = left, 2 = right
  118. $Horizontal = 0;
  119. $Vertical = 0;
  120. // See if any similar artists have been placed yet. If so, place artist in that half
  121. // (provided that there are enough in the other half to visually balance out)
  122. reset($Artist->Similar);
  123. foreach($Artist->Similar as $SimilarArtist) {
  124. list($Artist2ID) = array_values($SimilarArtist);
  125. if($this->Artists[$Artist2ID]) {
  126. if($this->Artists[$Artist2ID]->x > (WIDTH/2) && ($NumRight-$NumLeft)<1){
  127. $Horizontal = 2;
  128. } elseif($NumLeft-$NumRight<1) {
  129. $Horizontal = 1;
  130. }
  131. break;
  132. }
  133. }
  134. shuffle($xValues);
  135. while($xValue = array_shift($xValues)){
  136. if(abs($this->x - $xValue) <= $IdealDistance) {
  137. if(hypot(abs($this->x - $xValue), ($this->y - 50)) > $IdealDistance
  138. || ceil(sqrt(pow($IdealDistance, 2) - pow($this->x - $xValue, 2))) > (HEIGHT/2)){
  139. $xValue = $this->x - ceil(sqrt(pow($IdealDistance, 2) - pow($IdealDistance*0.1*rand(5,9), 2)));
  140. //echo "Had to change x value for ".$Artist->Name." to ".$xValue."\n";
  141. }
  142. // Found a match (Is close enough to the center to satisfy $IdealDistance),
  143. // Now it's time to choose which half to put it on
  144. if(!$Horizontal) {
  145. // No similar artists displayed
  146. $Horizontal = ($NumLeft<$NumRight) ? 1 : 2;
  147. }
  148. if($Horizontal == 2){
  149. $xValue = WIDTH-$xValue;
  150. $NumRight++;
  151. } else {
  152. $NumLeft++;
  153. }
  154. $Artist->x = $xValue;
  155. $this->xValues[$xValue] = $ArtistID;
  156. unset($xValues[$xValue]);
  157. break;
  158. }
  159. }
  160. if(!$xValue){ // Uh-oh, we were unable to choose an x value.
  161. $xValue = ceil(sqrt(pow($IdealDistance, 2)/2));
  162. $xValue = (WIDTH/2)-$xValue;
  163. $Artist->x = $xValue;
  164. $this->xValues[$xValue] = $ArtistID;
  165. unset($xValues[$xValue]);
  166. }
  167. // Pythagoras. $yValue is the vertical distance from the center to the y value
  168. $yValue = sqrt(pow($IdealDistance, 2) - pow(abs($this->x - $Artist->x), 2));
  169. // Now we pick if it should go on the top or bottom
  170. if($NumTop>$NumBottom){ // Send it to the bottom half
  171. $yValue=(HEIGHT/2)+$yValue;
  172. $NumBottom++;
  173. } else {
  174. $yValue=(HEIGHT/2)-$yValue;
  175. $NumTop++;
  176. }
  177. $yValue = ceil($yValue);
  178. // $yValue is now a proper y coordinate
  179. // Now time to do some spacing out
  180. if($yValue < 10){
  181. $yValue+=(10+abs($yValue))+rand(10,20);
  182. }
  183. if($yValue > (HEIGHT - 10)){
  184. $yValue-=((HEIGHT/2)-rand(10,20));
  185. }
  186. $i = 1;
  187. while($Conflict = $this->scan_array_range($this->yValues, abs($yValue-13), $yValue+13)) {
  188. if($i > 10){
  189. break;
  190. }
  191. if(!$this->scan_array_range($this->yValues, abs($yValue-5), $yValue-20)){
  192. $yValue -= 20;
  193. }
  194. $yValue=$Conflict + rand(10, 20);
  195. if($yValue>HEIGHT-10){
  196. $yValue-=ceil(HEIGHT/2.5);
  197. } elseif($yValue<10) {
  198. $yValue+=ceil(HEIGHT/2.5);
  199. }
  200. $i++;
  201. }
  202. $Artist->y = $yValue;
  203. $this->yValues[$yValue] = $ArtistID;
  204. }
  205. reset($this->Artists);
  206. reset($this->xValues);
  207. reset($this->yValues);
  208. }
  209. // Calculate the ideal distance from the center point ($Rootx, $Rooty) to the artist's point on the board
  210. // Pythagoras as fun!
  211. function calculate_distance($SimilarityCoefficient, $Rootx, $Rooty){
  212. $MaxWidth = WIDTH - $Rootx;
  213. $MaxHeight = HEIGHT - $Rooty;
  214. $x = $MaxWidth - ($SimilarityCoefficient*$MaxWidth*.01); // Possible x value
  215. $y = $MaxHeight - ($SimilarityCoefficient*$MaxHeight); // Possible y value
  216. $Hypot = hypot($Rootx - $x, $Rooty - $y);
  217. return $MaxWidth - $Hypot;
  218. }
  219. function similarity($Score, $TotalArtistScore){
  220. return (pow(($Score/($TotalArtistScore+1)), (1/1)));
  221. }
  222. function scan_array_range($Array, $Start, $Finish){
  223. if($Start<0){
  224. die($Start);
  225. }
  226. for ($i = $Start; $i<=$Finish; $i++){
  227. if(isset($Array[$i])){
  228. return $i;
  229. }
  230. }
  231. return false;
  232. }
  233. function write_artists(){
  234. ?>
  235. <div style="position:absolute;bottom:<?=$this->y-10?>px;left:<?=$this->x-(strlen($this->Name)*4)?>px;font-size:13pt;" class="similar_artist_header">
  236. <?=$this->Name?>
  237. </div>
  238. <?
  239. foreach($this->Artists as $Artist){
  240. if($Artist->ID == $this->ID){
  241. continue;
  242. }
  243. $xPosition = $Artist->x - (strlen($Artist->Name)*4);
  244. if($xPosition<0){
  245. $xPosition=3;
  246. $Artist->x = $xPosition;
  247. }
  248. $Decimal = $this->Similar[$Artist->ID]['Decimal'];
  249. if($Decimal<0.2){
  250. $FontSize = 8;
  251. } elseif($Decimal<0.3){
  252. $FontSize = 9;
  253. } elseif($Decimal<0.4){
  254. $FontSize = 10;
  255. } else {
  256. $FontSize = 12;
  257. }
  258. ?>
  259. <div style="position:absolute;top:<?=$Artist->y-5?>px;left:<?=$xPosition?>px;font-size:<?=$FontSize?>pt">
  260. <a href="artist.php?id=<?=$Artist->ID?>" class="similar_artist"><?=$Artist->Name?></a>
  261. </div>
  262. <?
  263. }
  264. reset($this->Artists);
  265. }
  266. function background_image(){
  267. global $Img;
  268. reset($this->Similar);
  269. foreach($this->Similar as $SimilarArtist) {
  270. list($ArtistID, $Val) = array_values($SimilarArtist);
  271. $Artist = $this->Artists[$ArtistID];
  272. $Decimal = $this->Similar[$ArtistID]['Decimal'];
  273. $Width = ceil($Decimal*4)+1;
  274. $Img->line($this->x, $this->y, $Artist->x, $Artist->y,$Img->color(199,218,255), $Width);
  275. unset($Artist->Similar[$this->ID]);
  276. reset($Artist->Similar);
  277. foreach($Artist->Similar as $SimilarArtist2) {
  278. list($Artist2ID) = array_values($SimilarArtist2);
  279. if($this->Artists[$Artist2ID]){
  280. $Artist2 = $this->Artists[$Artist2ID];
  281. $Img->line($Artist->x, $Artist->y, $Artist2->x, $Artist2->y,$Img->color(173,201,255));
  282. unset($Artist2->Similar[$ArtistID]);
  283. }
  284. }
  285. reset($this->xValues);
  286. }
  287. $Img->make_png(SERVER_ROOT.'/static/similar/'.$this->ID.'.png');
  288. }
  289. function dump(){
  290. echo "Similarities:\n";
  291. foreach($this->Artists as $Artist){
  292. echo $Artist->ID;
  293. echo ' - ';
  294. echo $Artist->Name;
  295. echo "\n";
  296. echo "x - ".$Artist->x."\n";
  297. echo "y - ".$Artist->y."\n";
  298. print_r($this->Similar[$Artist->ID]);
  299. //print_r($Artist->Similar);
  300. echo "\n\n---\n\n";
  301. }
  302. }
  303. }
  304. ?>