Задача: Плоттеры для рисования графиков
Исходник: Плоттер для рисования графиков. Пример - график sin-уса, язык: javascript [code #142, hits: 8159]
автор: - [добавлен: 22.05.2006]
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  2. <html>
  3. <head>
  4. <title>Function Plotter</title>
  5. <style type="text/css">
  6. .plotterarea {
  7. postitio: relative;
  8. width: 80%;
  9. height: 80%;
  10. overflow: hidden;
  11. background-color: #cccccc;
  12. left: center;
  13. border-left: 2px solid black;
  14. }
  15. .plotter_pen {
  16. position: absolute;
  17. background-color: black;
  18. margin:0px;
  19. width: 3px;
  20. height: 3px;
  21. font-size: 1px;
  22. z-index: 10;
  23. }
  24. .plotter_interpolation {
  25. position: absolute;
  26. background-color: #999999;
  27. margin:0px;
  28. width: 3px;
  29. height: 3px;
  30. font-size: 1px;
  31. z-index: 5;
  32. }
  33. </style>
  34. <script type="text/javascript">
  35. <!--
  36. /**
  37. * Контейнер для точек, что бы скрывать/удалять удобней было.
  38. */
  39. function PlotterDrawing(name, area) {
  40. this.points=[]; //контейнер с точками
  41. this.area=area; //предок, обьект PlotterArea
  42. this.name=name; //имя графика
  43. this.initing=false; //флаг инициализации, пока установлен флаг, скрыть или удалить график нельзя
  44. }
  45. /**
  46. * Скрыть/показать график
  47. * @param visible - true - показать, false - скрыть график
  48. */
  49. PlotterDrawing.prototype.setVisible=function(visible) {
  50. if(this.initing) return;
  51. for(var i=0; i<this.points.length; i++) this.points[i].style.visibility=visible? "true": "false";
  52. }
  53. /**
  54. * Удалить график
  55. */
  56. PlotterDrawing.prototype.remove=function() {
  57. if(this.initing) return;
  58. for(var i=0; i<this.points.length; i++) this.area.area.removeChild(this.points[i]);
  59. this.points=[];
  60. this.area.drawings["~"+this.name]=null;
  61. }
  62. /**
  63. * Если установить в true, то дополняем все несуществующие точки простейшей
  64. * интерполяцией. Не рекомендуеться для медленных машин.
  65. */
  66. PlotterArea.use_interpolation=true;
  67. /**
  68. * Создать активное поле для графиков. Аргументом передаём слой/блочный элемент
  69. * где рисуем граифк.
  70. */
  71. function PlotterArea(obj) {
  72. if(!obj||obj.nodeType!==1||obj.offsetWidth<1||obj.offsetHeight<1) throw "PlotterArea.<constructor> IlligalArgumentException: area is not a HTML element";
  73. this.area=obj;
  74. this.width=obj.offsetWidth
  75. this.height=obj.offsetHeight;
  76.  
  77. this.plotter=document.createElement("SPAN"); //дефолтовый мелок
  78. this.plotter.className="plotter_pen";
  79. this.plotter.style.visibility="hidden";
  80. this.area.appendChild(this.plotter);
  81.  
  82. this.drawings={}; //регистр графиков
  83. }
  84. /**
  85. * Нарисовать график. Аргументами передаём имя, функцию и опциональные параметры.
  86. * @param name - имя графика
  87. * @param func - функция, что будем рисовать
  88. * @param start - стартовое значение для аргумента функции
  89. * @param step - шаг, с которым будем проходить по функции
  90. * @param coef - выравнивающий коефициент, -1 <= func(arg)*coef <= 1
  91. */
  92. PlotterArea.prototype.draw=function(name, func, start, step, coef) {
  93. if(typeof(func)!="function"||name=="") throw "PlotterArea.draw: IlligalArgumentException: name and function must be specified";
  94. var d=this.getDrawing(name); //если уже существует такой график, то удалим
  95. this.drawings["~"+name]=(d=(d!=null)? (d.remove(),d): new PlotterDrawing(name, this));
  96. d.initing=true;
  97.  
  98. var steps=Math.floor(this.width/this.plotter.offsetWidth); //количество точек вмещаемых в область рисования
  99. start=start==null? 0: start;
  100. step=step==null? 1: step;
  101. coef=coef==null? 1: coef;
  102.  
  103. var pts=[];
  104. for(var i=0; i<steps; i++) pts.push(func(start+step*i)*coef); //must be [-1...1]
  105.  
  106. var prnt=this, pos=0, prevp=null;
  107. var tmr=window.setInterval(drawThread, 50); //ставим отрисовку с паузой в миллисекундах
  108.  
  109. //исполняемый тред, тот что будет рисовать точки
  110. function drawThread() {
  111. if(pos>=pts.length) {
  112. d.initing=false;
  113. window.clearTimeout(tmr);
  114. return;
  115. }
  116. var p=prnt.plotter.cloneNode(true);
  117. p.style.visibility="visible";
  118. p.style.left=(pos*prnt.plotter.offsetWidth)+"px";
  119. p.style.top=(prnt.height/2 - pts[pos]*prnt.height/2)+"px";
  120. prnt.area.appendChild(p);
  121. d.points.push(p);
  122. /*
  123. * Если разрешенна простая интерполяция, то дорисовываем недостающие точки.
  124. * Медленный процес под Мозиллой, не советую... =/
  125. */
  126. var bijsteps;
  127. if(PlotterArea.use_interpolation && prevp!=null && (bijsteps=(Math.abs(prevp.offsetTop-p.offsetTop)/prnt.plotter.offsetHeight))>1) {
  128. var sp=prnt.plotter.offsetWidth/bijsteps;
  129. var sign=(prevp.offsetTop>p.offsetTop)? -1: 1;
  130. var x, y, pi, prevx=prevp.offsetLeft;
  131. for(var i=0; i<bijsteps; i++) {
  132. x=Math.round(prevp.offsetLeft + sp*i);
  133. if(pi&&x==prevx) { //простенькая оптимизация, дабы не генерить кучу точек друг под дружкой
  134. pi.style.height=(pi.offsetHeight+prnt.plotter.offsetHeight)+"px";
  135. if(sign<0) pi.style.top=(pi.offsetTop-prnt.plotter.offsetHeight)+"px";
  136. } else {
  137. pi=prnt.plotter.cloneNode(true);
  138. pi.className="plotter_interpolation";
  139. pi.style.visibility="visible";
  140. pi.style.left=x+"px";
  141. y=Math.round(prevp.offsetTop + prnt.plotter.offsetHeight*sign*(i+1))
  142. pi.style.top=y+"px";
  143. if(sign<0&&y>prevp.offsetTop) alert(prevp.offsetTop+", "+y);
  144. prnt.area.appendChild(pi);
  145. d.points.push(pi);
  146. prevx=pi.offsetLeft;
  147. }
  148. }
  149. }
  150. prevp=p;
  151. pos++;
  152. }
  153. }
  154. /**
  155. * Поставить мелок. Аргументом принимаем HTML элемент, им может быть что угодно,
  156. * от картинки, до целой страницы(шутка ;-))
  157. */
  158. PlotterArea.prototype.setPlotter=function(pen) {
  159. if(!pen||pen.nodeType!=1) throw "PlotterArea.setPlotter: IlligalArgumentException: pen is not a HTML element";
  160. this.plotter=pen;
  161. }
  162. /**
  163. * Достать график по имени, полученный график можно затем скрыть или удалить.
  164. */
  165. PlotterArea.prototype.getDrawing=function(name) {
  166. return (typeof(this.drawings["~"+name])=="object")? this.drawings["~"+name]: null;
  167. }
  168. //=============================- Test -============================
  169. var area;
  170. function drawSinus() {
  171. area=new PlotterArea(document.getElementById("graph"));
  172. try {
  173. area.draw("sinus", Math.sin, 0, 0.1, 0.9);
  174. } catch(e) {
  175. alert(e.toString());
  176. }
  177. }
  178. function removeSinus() {
  179. if(area) {
  180. var d=area.getDrawing("sinus");
  181. if(d.initing) alert("График сейчас рисуеться, я бы не рискнул его остановить =))");
  182. else d.remove();
  183. }
  184. }
  185. //-->
  186. </script>
  187. </head>
  188. <body>
  189. <div style="width: 100%; height: 100%; text-align: center;">
  190. <h3>Движение по траектории синуса</h3>
  191. <button onclick="drawSinus()">Вперёд</button>&nbsp;<button onclick="removeSinus()">Удалить график</button><br><br>
  192. <div id="graph" class="plotterarea">
  193. <div style="position: absolute; font-size: 1px; width: 100%; height:2px; background-color: black; left: 0px; top: 50%;"</div>
  194. </div>
  195. </body>
  196. </html>
По точкам с определенным шагом рисует, интерполирует заданный график.

Найдено на forum.vingrad.ru
Тестировалось на: IE 6.0 SP2, Mozilla FF 1.5, Opera 8.5

+добавить реализацию