пятница, 12 октября 2012 г.

Реализация "бегающих" маркеров и линейки над стандартным TChart

Сегодня расскажу вам как немножко приукрасить стандартный TChart... Например для использования в составе бенчмарка для просмотра регистраций осциллографов Agilent MSO-60xx.


Отрывок авторского материала "Следим за космической погодой или...
NOAA.Геомагнитная активность Земли за последние 30 дней"
ж.Радиолюбитель, 2011, №11

Прежде всего, реализация метки, тут ничего сложного (показывает цвет, имя, координаты для серии из множества ...и собственно сам прицел):
procedure TForm1.chMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var i: integer;
    tmpX,tmpY:Double;
begin
 gx:=x; gy:=y;
 ch.Repaint;

 for i:=0 to ch.SeriesCount-1 do begin
  ch.Series[i].GetCursorValues(tmpX,tmpY);
  metka1:= ch.Series[i].GetHorizAxis.LabelValue(tmpX);
  metka2:= ch.Series[i].GetVertAxis.LabelValue(tmpY);


  sb.Panels[0].Text:= 'X= '+ ch.Series[i].GetHorizAxis.LabelValue(tmpX);
  sb.Panels[1].Text:= 'Y= '+ ch.Series[i].GetVertAxis.LabelValue(tmpY);

  if ch.Series[i].GetCursorValueIndex<>-1 then begin
   metka3:= ch.Series[i].Name;
   sb.Panels[2].Text:= 'Title: ' + metka3;

   metka4:= ColorToString(ch.Series[i].ValueColor[0]);
   metka4:= copy(metka4, 3, length(metka4));
   sb.Panels[3].Text:= 'Color: ' + metka4
  end

 end
end;

procedure TForm1.chAfterDraw(Sender: TObject);
var i: integer;
begin
 with (Sender as TChart).Canvas do begin
  Font.Size:= 8; Font.Color:= clsilver;
  // прицел/линейка
  if gx>1then begin
   pen.Color:=clsilver;
   moveto(gx,0);     lineto(gx,gy-15);
   moveto(gx,gy+15); lineto(gx,ch.Height);

   TextOut(gx+10,gy+font.Height-2,metka1);
   TextOut(gx+10,gy,metka2);
   TextOut(gx+10 + 50,gy+font.Height-2, metka3);
   TextOut(gx+10 + 50,gy, metka4);
  end;

  // текст-
  //for i:=0 to length(txt_)-1 do
  // TextOut(txt_x[i],txt_y[i],txt_[i]);
  // метка-
  {for i:=0 to length(metx_)-1 do begin
   Brush.Color:=clblack;
   Pen.Color:=clblue;
   Rectangle(met_x[i]-3,met_y[i]-3,met_x[i]+3,met_y[i]+3);
   Line(met_x[i],met_y[i],met_x[i]+5,met_y[i]-15);
   Line(met_x[i]+5,met_y[i]-15,met_x[i]+30,met_y[i]-15);
   Brush.Color:=clwhite;
   TextOut(met_x[i]+5,met_y[i]-33,mety_[i]);
   TextOut(met_x[i]+5,met_y[i]-43,metx_[i])
  end  }

 end
end;


Теперь же, рассмотрим вариант с точкой-маркером на графике (и усложним задачу для n-го набора серий ). Добавим в Chart условную серию Point, которая будет хранить столько маркерных точек сколько нам нужно:
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
 // тестовые серии-
 randomize;
 for i:=0 to 300 do begin
  series1.AddXY(i, 4*sin(4*i*pi/180));
  series2.AddXY(i, 4*sin(i*pi/180));
  series3.AddXY(i, 4*cos(i*pi/180))
 end;
 series1.LinePen.Width:= 2;
 series3.LinePen.Width:= 3;

 // три графика - 3 маркера
 series4.AddXY(0,0, '', clred);
 series4.AddXY(0,0, '', clgreen);
 series4.AddXY(0,0, '', clyellow);

 series4.Title:= 'Metka';
end;
Передвигать каждый из маркеров мы сможем просто задавая координаты SeriesX.XValue[i], SeriesX.YValue[i] (где i- номер маркера). А теперь модифицируем код, приведенный выше, следующим образом:
procedure TForm1.chMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var i: integer;
    tmpX,tmpY:Double;
    xk, yk: double;  // координата xk:yk серии
begin
 gx:=x; gy:=y;
 ch.Repaint;

 for i:=0 to 2{ch.SeriesCount-1} do begin
  ch.Series[i].GetCursorValues(tmpX,tmpY); // переводим координаты из позиции курсора
  metka1:= ch.Series[i].GetHorizAxis.LabelValue(tmpX);
  metka2:= ch.Series[i].GetVertAxis.LabelValue(tmpY);


  sb.Panels[0].Text:= 'X= '+ ch.Series[i].GetHorizAxis.LabelValue(tmpX);
  sb.Panels[1].Text:= 'Y= '+ ch.Series[i].GetVertAxis.LabelValue(tmpY);

  if ch.Series[i].GetCursorValueIndex<>-1 then begin
   metka3:= ch.Series[i].Name;
   sb.Panels[2].Text:= 'Title: ' + metka3;

   metka4:= ColorToString(ch.Series[i].ValueColor[0]);
   metka4:= copy(metka4, 3, length(metka4));
   sb.Panels[3].Text:= 'Color: ' + metka4;
  end;

  // бегающие маркеры -----------------------------------------------------
  // текущее значение по оси X

  xk:= strtofloat(ch.Series[i].GetHorizAxis.LabelValue(tmpX));
  // выясняем значение графика в nk- отсчете
  yk:= getlocate(ch.Series[i], xk);
  // позиция маркера-
  if (xk< ch.BottomAxis.Maximum)and(xk>= ch.BottomAxis.Minimum-0.1) then begin //выход за пределы
   ch.Series[3].XValue[i]:= xk;
   ch.Series[3].YValue[i]:= yk
  end
  // end бегающие маркеры ----------------------------------

 end
end;
Поскольку функа Locate() в реализации стандартного TChart подглючивает, то заменим ее следующим образом (смысл, думаю, ясен):
// подмена LOCATE TCHART
function getlocate(s: TchartSeries; x: double): double;
var i: integer;
begin
 result:= 0;

 for i:= 0 to s.YValues.Count-1 do
  if s.XValue[i]>= x then begin
    result:= s.YValue[i];
    break
  end
end;

Компильнем, и получим отслеживание маркерами по каждой из своих серий при перемещении позиции мыши над Chart-ом:


Вуа-ля! Имеем реализацию задуманного над стандартным TChart.

Постскриптум

Следует отметить, что пример с дополнительными сериями для маркеров - утрированный для наглядности. Для визуализации маркеров плодить серии вовсе нет необходимости, достаточно отрисовывать их самому прямо на канве, а параметры хранить в динамическом массиве.

Ссылки по тематике
  1. Реализация "бегающих" маркеров и линейки над стандартным TChart 
  2. Просмотр регистраций осциллографов Agilent MSO-60xx 
  3. Просмотр регистраций осциллографов Tektronix MSO-40xx
  4. Анализатор спектра регистраций осциллографов Agilent MSO-60хх/Tektronix MSO-40хх
  5. Интерактивные маркеры для анализатора спектра регистраций осциллографов Agilent MSO-60хх/Tektronix MSO-40хх

    забрать или забрать добро себе 

    Комментариев нет:

    Отправить комментарий

    В комментариях уважайте собеседника, внимательно читайте посты и не додумывайте. Просьбы и предложения из разряда: «можно ваш Skype/Viber/телефон», «напишите мне в vk/FB», а также другие им подобные — игнорируются. Выход новых версий ПО, внешняя ссылка, переставшая работать с течением времени и т.п. не является основанием для претензий. Желающие спокойно подискутировать и высказаться — Welcome. Желающие спонсировать блог — Donate. Нарушение этих простых правил ведет к бану и удалению комментариев без предупреждения.