Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 27.01.2011, 02:00   #1
Arkuz
Форумчанин
 
Аватар для Arkuz
 
Регистрация: 22.09.2007
Сообщений: 540
Восклицание Canvas, код готов. Пропорции.

Здравствуйте.

Вот появилась необходимость чертить графики. TChart по ряду некоторых причин не подходит. Решено было всё делать на Канве.

Методика следующая.
Берём канву, обязательное условие, чтобы она была квадратная.
Отсюда следует первая проблема – канва 330х330, а координаты могут быть в пределах 100 000.
Решаю следующим образом. Выбираю максимальное значение X или Y перебирая все точки. Выбрав максимум, допустим 35 000. Приводим это всё дело для отображения на канве к пропорции:

Длина и ширина канвы 330 - максимальному значению точки 35 000
Размещение на канве значения координаты ? - допустим 28 500

Иными словами пропорция
330 = 35 000
? = 28500

Отсюда неизвестное значение примерно равно 271, как раз значение для любой оси на канве

330 = 35 000
271 = 28500

330 = 100%
271 = 82%

Т.е. я просто пропорционально переношу большую координату на канву.
Со значениями в пределах 40 000 график выглядит как на рисунке 1

И мне нужно приблизить как-либо линию, ну просто чтобы она рисовалась уже в приближенном виде. См. рисунок 2

P.S. Оси координат специально поменяны местами, этого требует задача.

код
Код:
with Image1 do
        begin
        //очистка канвы
        Canvas.Pen.Color:=clWhite;
        Canvas.Rectangle(0,0,330,330);
        Canvas.Refresh;

        //задаём параметры
        //рисуем сетку
        Canvas.Pen.Style:=psSolid;
        Canvas.Pen.Color:=clBlue;
        Canvas.Pen.Width:=1;
        i:=0;
        while i<>330 do
        begin
          Canvas.MoveTo(i,1);
          Canvas.LineTo(i,330);
          Canvas.MoveTo(1,i);
          Canvas.LineTo(330,i);
          i:=i+30;
        end;
        //--------------

        //отрисовка осей координат и X,Y
        Canvas.Pen.Width:=3;
        Canvas.TextOut(10,10,'X');
        Canvas.TextOut(310,310,'Y');
        Canvas.MoveTo(1,1);
        Canvas.LineTo(1,329);
        Canvas.LineTo(329,329);
        Canvas.Pen.Color:=clRed; 
        //----------------

        // определяем xmin
        if Points.p1.x>Points.p2.x
          then
           begin
             xmin:=Points.p2.x;
             xmax:=Points.p1.x;
           end
          else
           begin
             xmin:=Points.p1.x;
             xmax:=Points.p2.x;
           end;
        // определяем ymin
        if Points.p1.y>Points.p2.y
          then
           begin
             ymin:=Points.p2.y;
             ymax:=Points.p1.y;
           end
          else
           begin
             ymin:=Points.p1.y;
             ymax:=Points.p2.y;
           end;
        //определяем максимальную координату для масштаба
        if xmax>ymax then t:=xmax
                     else t:=ymax;
        // масштаб = max координата + координата/10
        t:=t+round(t/5);
        // подписываем масштаб в правом верхнем углу
        Canvas.TextOut(280,10,'1:'+IntToStr(round(t)));

        //переменная содержащая длину канвы
        //канва квадратная
        cwh:=Width;
        
        //вычисляем координаты точек на канве
        //пропорционально размерам канвы
        x:=(cwh-round((Points.p1.x*cwh)/t));
        y:=(round((Points.p1.y*cwh)/t));
        x1:=(cwh-round((Points.p2.x*cwh)/t));
        y1:=(round((Points.p2.y*cwh)/t));

        //проводим линию
        Canvas.MoveTo(y,x);
        Canvas.LineTo(y1,x1);

//для правильного размещения подписей
        cwh:=round(cwh/2);
        if (x<cwh) and (y<cwh) then Canvas.TextOut(y+length(FPNEdit.Text)*3,x+5,FPNEdit.Text);
        if (x>cwh) and (y<cwh) then Canvas.TextOut(y+length(FPNEdit.Text)*3,x-15,FPNEdit.Text);
        if (x<cwh) and (y>cwh) then Canvas.TextOut(y-length(FPNEdit.Text)*3,x+5,FPNEdit.Text);
        if (x>cwh) and (y>cwh) then Canvas.TextOut(y-length(FPNEdit.Text)*3,x-15,FPNEdit.Text);

        if (x1<cwh) and (y1<cwh) then Canvas.TextOut(y1+length(SPNEdit.Text)*3,x1+5,SPNEdit.Text);
        if (x1>cwh) and (y1<cwh) then Canvas.TextOut(y1+length(SPNEdit.Text)*3,x1-15,SPNEdit.Text);
        if (x1<cwh) and (y1>cwh) then Canvas.TextOut(y1-length(SPNEdit.Text)*3,x1+5,SPNEdit.Text);
        if (x1>cwh) and (y1>cwh) then Canvas.TextOut(y1-length(SPNEdit.Text)*3,x1-15,SPNEdit.Text);

        end;
Изображения
Тип файла: jpg 1.JPG (36.8 Кб, 192 просмотров)
Тип файла: jpg 2.JPG (42.3 Кб, 189 просмотров)
Arkuz вне форума Ответить с цитированием
Старый 27.01.2011, 10:10   #2
SERG1980
Участник клуба
 
Аватар для SERG1980
 
Регистрация: 28.03.2007
Сообщений: 1,814
По умолчанию

Судя по предыдущему посту вам chart как раз подойдёт. У него куча настроек. А вот с канвой будет геморой. Поизучайте этот комопнент, там ниччего сложного. А чтобы у вас всегда был квадратным уберите галку автомасштабирование и выставите какой вам надо + Chart1.MaxPointsPerPage:=0;
SERG1980 вне форума Ответить с цитированием
Старый 27.01.2011, 14:07   #3
Arkuz
Форумчанин
 
Аватар для Arkuz
 
Регистрация: 22.09.2007
Сообщений: 540
По умолчанию

Суть в том, что в Чарте есть сетка, и как раз эта сетка постоянно меняется, надо чтобы всегда сетка квадратная была.
Arkuz вне форума Ответить с цитированием
Старый 27.01.2011, 14:20   #4
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Если не ошибаюсь в Чарте всетки можно настроить сетку стабильно и жестко...
Покрутись в дизайнере в его свойствах.
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 28.01.2011, 13:22   #5
InternetStranger
php / delphi
Форумчанин
 
Аватар для InternetStranger
 
Регистрация: 10.06.2007
Сообщений: 175
По умолчанию

Поскольку Вы (вроде бы) не в первый раз пишете с этой проблемой, решил помочь. TChart действительно обладает чрезвычайно большим количеством настроек, к сожалению настолько большим, что тяжело в них ориентироваться (с программной стороны).

Думаю этот пример сгодится для вас. Здесь масштаб по вертикальной оси задается изначально, а по горизонтальной подстраивается автоматически из условия квадратности ячеек. Код программы со скомпилированным *.exe приложен.
Код:
// --- Вы можете рассчитать эти значения программно (предварительно)
CONST
     AmplitudeX = 10;                     // Амплитуда (размах) по Горизонтальной Оси  OX
     AmplitudeY = 3;                      // Амплитуда (размах) по Вертикальной Оси  OY
     MarkersCountY = AmplitudeY*2;        // Кол-во делений (штрихов на оси) по Вертикальной Оси    OY
VAR
     Time          : Real;                // Аргумент функции

Function F( time: Real ): Real;           // Собсвенно функция
Begin
     Result := sin( time );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
     Time := 0;
end;

// --- Кнопка "Поехали/Стоп"
procedure TForm1.btn1Click(Sender: TObject);
var PixPerCellY    : Integer;
    StartPx, EndPx : Real;
begin
     // --- Настраиваем Chart при запуске
     if Time = 0 then begin
         // -- Вертикальная ось
         With Chart1.LeftAxis do begin
              Automatic   := False;
              Minimum     := - 0.5*AmplitudeY;
              Maximum     := + 0.5*AmplitudeY;
              Repaint;                                       // ОБЯЗАТЕЛЬНО вызвать перепостроение. Чтобы изменения выше применились к оси
              Increment   := (Maximum-Minimum)/MarkersCountY;
              StartPx     := CalcPosValue( Minimum );                          // Начало оси в пиксельных координатах
              EndPx       := CalcPosValue( Maximum );                          // Конец оси в пиксельных координатах
              PixPerCellY := Round( Abs(EndPx-StartPx) / ( (Maximum-Minimum)/Increment ));  // Кол-во пикселей, приходящихся на одну ячейку по OY
         end;

         // -- Горизональная ось
         With Chart1.BottomAxis do begin
              Automatic   := False;
              Minimum     := 0;
              Maximum     := AmplitudeX;
              Repaint;
              StartPx     := CalcPosValue( Minimum );
              EndPx       := CalcPosValue( Maximum );                          
              Increment   := PixPerCellY*(Maximum-Minimum) /  Abs(EndPx-StartPx); // Шаг по оси из условия квадратности ячейки (т.е. должно помещаться столько же пикселей = PixPerCellY)
         end;
     end;
     
     Timer1.Enabled := not Timer1.Enabled;
end;

// --- Кнопка "Очистка"
procedure TForm1.btn2Click(Sender: TObject);
begin
     Chart1.Series[0].Clear;
end;

// --- Построение в Таймере
procedure TForm1.Timer1Timer(Sender: TObject);
begin
     // -- Если значение по оси OX стало больше, чем положено -> сдвигаем график в право (чтобы сохранить ячейки квадратными)
     if CheckBox1.Checked and (Time > AmplitudeX) then begin
        Chart1.BottomAxis.Maximum := Time;
        Chart1.BottomAxis.Minimum := Time - AmplitudeX;
     end;

     Chart1.Series[0].AddXY( Time, F(Time) );
     Time := Time + 0.05;
end;

Это пример визуальной настройки осей:


ps: Еще рекомендую скачать с офф.сайта разработчика User Guide and Tutorials v8 (TeeChart User Guide, Tutorials and Help for TeeChart VCL v8 (3.75 MB)).

Там и вся справка по компоненту + есть Юзер-Гуайд, в котором статья "Custom drawing on the Chart" (там хорошо все разбирается).
Изображения
Тип файла: jpg Скрин.jpg (220.1 Кб, 212 просмотров)
Вложения
Тип файла: rar ProgrammersForum - TChart.rar (223.9 Кб, 35 просмотров)
G.Azamat { Web Development / Computer simulation }
Начинающий программист думает, что в килобайте 1000 байтов, а законченный уверен, что в километре 1024 метра.
InternetStranger вне форума Ответить с цитированием
Старый 28.01.2011, 13:26   #6
SERG1980
Участник клуба
 
Аватар для SERG1980
 
Регистрация: 28.03.2007
Сообщений: 1,814
По умолчанию

Цитата:
Сообщение от Arkuz Посмотреть сообщение
Суть в том, что в Чарте есть сетка, и как раз эта сетка постоянно меняется, надо чтобы всегда сетка квадратная была.
Потому что по умолчанию работает автомасштабтрование. Я вам уже писал об этом. Отключите его и выставите нужный вам диапазон
SERG1980 вне форума Ответить с цитированием
Старый 28.01.2011, 13:34   #7
InternetStranger
php / delphi
Форумчанин
 
Аватар для InternetStranger
 
Регистрация: 10.06.2007
Сообщений: 175
По умолчанию

Цитата:
Сообщение от SERG1980 Посмотреть сообщение
Потому что по умолчанию работает автомасштабтрование. Я вам уже писал об этом.
Проблема-то не только в этом (точнее совсем не в этом). Автор хотел, как я понимаю, получить физически квадратные ячейки (в пикселях экрана). Чтобы, скажем, круг, выглядел кругом, а не эллипсом. Проблема как раз в том, как найти пиксельные координаты начала и конца осей на панели Чарта.
G.Azamat { Web Development / Computer simulation }
Начинающий программист думает, что в килобайте 1000 байтов, а законченный уверен, что в километре 1024 метра.
InternetStranger вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Нужно исправить код. Он готов, но нужна проверка и доработка. Forbesii Фриланс 2 24.12.2010 23:09
Готов ли я к изучению С++? fs444 Свободное общение 3 18.09.2009 10:23
Вставка картинки реальных размеров или хотябы в пропорции Mbus Microsoft Office Excel 4 02.12.2008 09:55