Модераторы: Sardar, Aliance
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Простой плоттер для рисования графиков, Ничего полезного, но возможно интересно 
:(
    Опции темы
Sardar
Дата 27.5.2005, 02:40 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 3
Всего: 317



Началось с этой темы: http://forum.vingrad.ru/index.php?showtopic=53244
Решил накидать простенький плотер для рисования графиков. Не скажу что получилось очень изящно, тем более разметка глючит под Мозиллой(это её глюки, спрашивать в разделе "Вёрстка", подстраиваться под неё времени не было).

Код коментировал, всё должно быть ясно. Интересные моменты: построение обьектов(PlotterArea, PlotterDrawing) и запуск вложенной функции "как тред", т.е. нормальная реализация задержки.

Код
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
    <title>Function Plotter</title>
<style type="text/css">
.plotterarea {
   postitio: relative;
   width: 80%;
   height: 80%;
   overflow: hidden;
   background-color: #cccccc;
   left: center;
   border-left: 2px solid black;
}
.plotter_pen {
   position: absolute;
   background-color: black;
   margin:0px;
   width: 3px;
   height: 3px;
   font-size: 1px;
   z-index: 10;
}
.plotter_interpolation {
   position: absolute;
   background-color: #999999;
   margin:0px;
   width: 3px;
   height: 3px;
   font-size: 1px;
   z-index: 5;
}
</style>
<script type="text/javascript">
<!--
/**
* Контейнер для точек, что бы скрывать/удалять удобней было.
*/
function PlotterDrawing(name, area) {
  this.points=[]; //контейнер с точками
  this.area=area; //предок, обьект PlotterArea
  this.name=name; //имя графика
  this.initing=false; //флаг инициализации, пока установлен флаг, скрыть или удалить график нельзя
}
/**
* Скрыть/показать график
* @param visible  - true - показать, false - скрыть график
*/
PlotterDrawing.prototype.setVisible=function(visible) {
  if(this.initing) return;
  for(var i=0; i<this.points.length; i++) this.points[i].style.visibility=visible? "true": "false";
}
/**
* Удалить график
*/
PlotterDrawing.prototype.remove=function() {
  if(this.initing) return;
  for(var i=0; i<this.points.length; i++) this.area.area.removeChild(this.points[i]);
  this.points=[];
  this.area.drawings["~"+this.name]=null;
}

/**
* Если установить в true, то дополняем все несуществующие точки простейшей
* интерполяцией. Не рекомендуеться для медленных машин.
*/
PlotterArea.use_interpolation=true;
/**
* Создать активное поле для графиков. Аргументом передаём слой/блочный элемент
* где рисуем граифк.
*/
function PlotterArea(obj) {
  if(!obj||obj.nodeType!==1||obj.offsetWidth<1||obj.offsetHeight<1) throw "PlotterArea.<constructor> IlligalArgumentException: area is not a HTML element";
  this.area=obj;
  this.width=obj.offsetWidth
  this.height=obj.offsetHeight;
  
  this.plotter=document.createElement("SPAN"); //дефолтовый мелок
  this.plotter.className="plotter_pen";
  this.plotter.style.visibility="hidden";
  this.area.appendChild(this.plotter);
  
  this.drawings={}; //регистр графиков
}
/**
* Нарисовать график. Аргументами передаём имя, функцию и опциональные параметры.
* @param name  - имя графика
* @param func  - функция, что будем рисовать
* @param start - стартовое значение для аргумента функции
* @param step  - шаг, с которым будем проходить по функции
* @param coef  - выравнивающий коефициент, -1 <= func(arg)*coef <= 1
*/
PlotterArea.prototype.draw=function(name, func, start, step, coef) {
  if(typeof(func)!="function"||name=="") throw "PlotterArea.draw: IlligalArgumentException: name and function must be specified";
  var d=this.getDrawing(name); //если уже существует такой график, то удалим
  this.drawings["~"+name]=(d=(d!=null)? (d.remove(),d): new PlotterDrawing(name, this));
  d.initing=true;
  
  var steps=Math.floor(this.width/this.plotter.offsetWidth); //количество точек вмещаемых в область рисования
  start=start==null? 0: start;
  step=step==null? 1: step;
  coef=coef==null? 1: coef;
  
  var pts=[];
  for(var i=0; i<steps; i++) pts.push(func(start+step*i)*coef); //must be [-1...1]
  
  var prnt=this, pos=0, prevp=null;
  var tmr=window.setInterval(drawThread, 50); //ставим отрисовку с паузой в миллисекундах
  
  //исполняемый тред, тот что будет рисовать точки
  function drawThread() {
    if(pos>=pts.length) {
      d.initing=false;
      window.clearTimeout(tmr);
      return;
    }
    var p=prnt.plotter.cloneNode(true);
    p.style.visibility="visible";
    p.style.left=(pos*prnt.plotter.offsetWidth)+"px";
    p.style.top=(prnt.height/2 - pts[pos]*prnt.height/2)+"px";
    prnt.area.appendChild(p);
    d.points.push(p);

    /*
    * Если разрешенна простая интерполяция, то дорисовываем недостающие точки.
    * Медленный процес под Мозиллой, не советую... =/
    */
    var bijsteps;
    if(PlotterArea.use_interpolation && prevp!=null && (bijsteps=(Math.abs(prevp.offsetTop-p.offsetTop)/prnt.plotter.offsetHeight))>1) {
      var sp=prnt.plotter.offsetWidth/bijsteps;
      var sign=(prevp.offsetTop>p.offsetTop)? -1: 1;
      var x, y, pi, prevx=prevp.offsetLeft;
      for(var i=0; i<bijsteps; i++) {
         x=Math.round(prevp.offsetLeft + sp*i);
         if(pi&&x==prevx) { //простенькая оптимизация, дабы не генерить кучу точек друг под дружкой
            pi.style.height=(pi.offsetHeight+prnt.plotter.offsetHeight)+"px";
            if(sign<0) pi.style.top=(pi.offsetTop-prnt.plotter.offsetHeight)+"px";
         } else {
            pi=prnt.plotter.cloneNode(true);
            pi.className="plotter_interpolation";
            pi.style.visibility="visible";
            pi.style.left=x+"px";
            y=Math.round(prevp.offsetTop + prnt.plotter.offsetHeight*sign*(i+1))
            pi.style.top=y+"px";
            if(sign<0&&y>prevp.offsetTop) alert(prevp.offsetTop+", "+y);
         prnt.area.appendChild(pi);
            d.points.push(pi);
            prevx=pi.offsetLeft;
         }
      }
    }
    prevp=p;
    pos++;
  }
}
/**
* Поставить мелок. Аргументом принимаем HTML элемент, им может быть что угодно,
* от картинки, до целой страницы(шутка ;-))
*/
PlotterArea.prototype.setPlotter=function(pen) {
  if(!pen||pen.nodeType!=1) throw "PlotterArea.setPlotter: IlligalArgumentException: pen is not a HTML element";
  this.plotter=pen;
}
/**
* Достать график по имени, полученный график можно затем скрыть или удалить.
*/
PlotterArea.prototype.getDrawing=function(name) {
  return (typeof(this.drawings["~"+name])=="object")? this.drawings["~"+name]: null;
}

//=============================- Test -============================
var area;
function drawSinus() {
  area=new PlotterArea(document.getElementById("graph"));
  try {
    area.draw("sinus", Math.sin, 0, 0.1, 0.9);
  } catch(e) {
    alert(e.toString());
  }
}
function removeSinus() {
  if(area) {
    var d=area.getDrawing("sinus");
    if(d.initing) alert("График сейчас рисуеться, я бы не рискнул его остановить =))");
    else d.remove();
  }
}
//-->
</script>
</head>

<body>
<div style="width: 100%; height: 100%; text-align: center;">
<h3>Движение по траектории синуса</h3>
<button onclick="drawSinus()">Вперёд</button>&nbsp;<button onclick="removeSinus()">Удалить график</button><br><br>
<div id="graph" class="plotterarea">
<div style="position: absolute; font-size: 1px; width: 100%; height:2px; background-color: black; left: 0px; top: 50%;"</div>
</div>

</body>
</html>


Это сообщение отредактировал(а) Sardar - 27.5.2005, 17:11


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Ciber SLasH
Дата 27.5.2005, 03:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1813
Регистрация: 9.11.2004
Где: С.-Петербург

Репутация: 1
Всего: 67



10х smile
Завтра (т.е. уже сегодня) буду разбираться в скрипте.

Но один баг уже нашёл: график получился не по синусу ! У синуса при положительных Х-ах первый полупериод -- положительный, а у тебя получился отрицательный.
PM   Вверх
Aliance
Дата 27.5.2005, 11:34 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 7
Всего: 137



Вот, нашел неплохую библиотеку по работе с графикой: http://srccode.spb.ru/?setp=1004
PM MAIL WWW ICQ Skype   Вверх
Sardar
Дата 27.5.2005, 17:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 3
Всего: 317



Ciber SLasH да это бага, т.к. отсчитывал координаты от верхнего левого угла, вечер был и жара smile
Пофиксил.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Ciber SLasH
Дата 31.5.2005, 03:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1813
Регистрация: 9.11.2004
Где: С.-Петербург

Репутация: 1
Всего: 67



2Aliance: 10x за ссылку на библиотеку, отличная библиотека. smile

2Sardar:
Я вот ваяю скриптик тоже похожий на твой "плоттер", но у меня он рисует по уже готовым координатам, которые предварительно загоняются в массив.
Вроде бы сделал, но мне кажется что слишком муторно я преобразовываю координаты графика в координаты экрана.

Вот формулы, которые я использую для преобразования координат графика в координаты области отрисовки:
Код

Xmax = x2 - x1  // max значения графика по X
Ymax = y2 - y1  // max значения графика по Y
mx, my          // ширина и длина области для отрисовки
cx, cy          // начало координат
sx = (mx - cx) / Xmax    // масштабирование по X
sy = (my - cy) / Ymax    // масштабирование по Y
X = Trunc(X * sx) + cx   // преобразование X-координаты графика
Y = cy - Trunc(Y * sy)   // преобразование Y-координаты графика
step = Xmax / (mx - cx)  // шаг графика, его в данном примере не использую

Вот то что у меня вышло (покамест использую чужую, мною урезанную, библиотеку для отрисовки линий графика. Библиотека, ссылку на которую дал Aliance):
Код

<html>
<head>
<meta HTTP-EQUIV='Content-Type' CONTENT='text/html;charset=windows-1251'>
<meta NAME=Generator CONTENT='EditPlus 2.12'>
<meta NAME=Author CONTENT='Ciber SLasH'>
<title>Отрисовка графиков по известным координатам</title>
<script language='JavaScript' src=graph.js></script><!-- графическая библиотека -->
<script language='JavaScript' src=coord.js></script><!-- файл с координатами графика -->
<script language='JavaScript'>
points = pts2    // начальный массив координат
pt = [[], []]    // отображаемый массив координат [[X], [Y]]
cnv = 0          // канва
ptCount = 50     // max кол-во точек на отрисовываемом графике
mx = 800         // ширина канвы
my = 500         // длина канвы
Xmax = Ymax = 0  // max значения графика по X и Y
cx = 0           // Х-координата начала отсчёта
cy = my - 200    // Y-координата начала отсчёта
sx = sy = 0      // масштабирование по осям X и Y

/*** Создание канвы ***/
function createCanvas() {
  cnv = createImage(mx, my)
  cnv.setStyle('blue', 'black')
}

/*** Отрисовка графика ***/
function plotGraph() {
var MinX,MaxX, MinY,MaxY  // min и max значения по осям X и Y
var i, Len
var x1,y1, x2,y2

  // Записываем в массив "pt" только ptCount-элементов из массива "points"
  //== BEGIN ===================================================================
  Len = points[0].length
  step = (Len > ptCount) ? Math.floor(Len / ptCount) : 1
  for (i = 0; i < Len; i += step) {
    pt[0].push(points[0][i])
    pt[1].push(points[1][i])
  }
  //== END =====================================================================

  // Поиск max и min значений по осям X и Y
  //== BEGIN ===================================================================
  MinX = MaxX = pt[0][0]
  MinY = MaxY = pt[1][0]
  Len = pt[0].length
  for (i = 0; i < Len; i++) {
    if (pt[0][i] < MinX) MinX = pt[0][i]
    else if (pt[0][i] > MaxX) MaxX = pt[0][i]

    if (pt[1][i] < MinY) MinY = pt[1][i]
    else if (pt[1][i] > MaxY) MaxY = pt[1][i]
  }
  //== END =====================================================================

  Xmax = MaxX - MinX       // max значение графика по Х
  Ymax = MaxY - MinY       // max значение графика по Y
  sx = (mx-cx) / Xmax      // масштабирование по X
  sy = (my-cy) / Ymax * 2  // масштабирование по Y

  // Отрисовка графика
  //== BEGIN ===================================================================
  x1 = pt[0][0]
  y1 = pt[1][0]
  cnv.Clear()
  cnv.setBoldLine(2)
  for (i = 1; i < Len; i++) {
    // Преобразование координат
    x2 = Math.floor(pt[0][i] * sx + cx)  // X = Trunc(X * sx) + cx
    y2 = Math.floor(cy - pt[1][i] * sy)  // Y = cy - Trunc(Y * sy)

    cnv.Line(x1,y1, x2,y2, 'red')  // подготавливаем линию
    cnv.Draw()                     // рисуем её
    x1 = x2
    y1 = y2
  }
  //== END =====================================================================
}
</script>
</head>

<body>
<table width=100%>
<tr><td align=center><button onClick=plotGraph()>Отрисовать график</button>
<tr><td align=center><script language='JavaScript'>createCanvas()</script>
</table>
</body>
</html>

Два доп. сккрипта в аттаче.

Собственно вопрос: какие формулы ты используешь для преобразования координат графика в координаты экрана ?

Это сообщение отредактировал(а) Ciber SLasH - 31.5.2005, 03:15

Присоединённый файл ( Кол-во скачиваний: 14 )
Присоединённый файл  grapher.zip
PM   Вверх
sergejzr
Дата 2.6.2005, 15:39 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 1
Всего: 360



Вот ещё библотечка
http://www.lutanho.net/diagram/index.html

Это сообщение отредактировал(а) Aliance - 2.6.2005, 15:56


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
Aliance
Дата 2.6.2005, 15:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 7
Всего: 137



sergej.z
что-то она мне неверно синусоиду построила =/
PM MAIL WWW ICQ Skype   Вверх
sergejzr
Дата 14.6.2005, 22:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 1
Всего: 360



Всё верно рисует smile где ошибка у тебя? Там можно всё самому настраиваить. По умолчанию ординада и абсцисса пересекаются в середине рисунка.
http://www.lutanho.net/diagram/dynamic_example.html


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
Aliance
Дата 16.6.2005, 12:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 7
Всего: 137



sergej.z
Вместо синусоиды - косинусоиду... вотъ =)
PM MAIL WWW ICQ Skype   Вверх
sergejzr
Дата 20.6.2005, 12:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 1
Всего: 360



разве у тебя не такой рисунок при таких параметрах? (Я амплитуду немного уменьшил, чтобы всё на экран помещалось) Посмотри внимательно, где нули пересекаются smile


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
Elfet
Дата 24.8.2005, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Белый и Пушистый
****


Профиль
Группа: Awaiting Authorisation
Сообщений: 3776
Регистрация: 2.4.2003

Репутация: нет
Всего: 16



Sardar
ха! супер!


--------------------
PM MAIL WWW Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Применение библиотек | Следующая тема »


 




[ Время генерации скрипта: 0.0808 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.