пятница, 16 декабря 2016 г.

Программа построения 4-х графиков

Задание:

1. Разработать подробный алгоритм построения 4х графиков на одном экране.
(4 функции для построения графиков придумать самостоятельно)

2. Алгоритм представить в содержательном виде, или в виде блок схемы, или в виде программного кода.

Решение:

Для разработки программы был выбран язык Delphi(Object Pascal).

Для разработки интерфейса использован кроссплатформенный фреймворк FMX (Fire-Monkey), что позволяет перекомпиляцией проекта получать рабочие бинарные файлы для следующих платформ: Windows32, Windows64, MacOS, Android, iOS.

Программа позволяет настроить область отображения графика, и параметры осей, кроме того область просмотра можно перемещать с помощью мыши.

Исходный код и скомпилированное приложение:

https://github.com/AnotherStudent/Draw4Graphics
https://raw.githubusercontent.com/AnotherStudent/Draw4Graphics/master/ProjectGraph.exe

Скриншоты работы:


Исходный текст:



// 2016 Peter. S.
//
// License:
//
// The MIT License (MIT)
// Copyright © 2016 by Peter S. (http://anotheritstudent.blogspot.com/)
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, 
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or 
// substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Программа-пример построения 4-х графиков функций.
// С помощью мыши можно исследовать функции.  

unit Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation,
  FMX.StdCtrls, FMX.Objects, FMX.Edit, FMX.EditBox, FMX.NumberBox;

type
  TMainForm = class(TForm)
    Disp: TPaintBox;
    Panel1: TPanel;
    nbMinX: TNumberBox;
    Label1: TLabel;
    Label2: TLabel;
    nbMinY: TNumberBox;
    Label3: TLabel;
    nbMaxX: TNumberBox;
    Label4: TLabel;
    nbMaxY: TNumberBox;
    Label5: TLabel;
    nbDotX: TNumberBox;
    Label6: TLabel;
    nbDotY: TNumberBox;
    procedure DispPaint(Sender: TObject; Canvas: TCanvas);
    procedure ParamsChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure DispMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure DispMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    procedure DispMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
  private
    MinX, MaxX, DotX: Single;
    MinY, MaxY, DotY: Single;
    SX, SY: Single;
    IsMove: Boolean;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.fmx}

uses
  System.Math;

type
  TFunction = reference to function (X: Single; var Y: Single): Boolean;

procedure TMainForm.DispMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  SX := X;
  SY := Y;
  IsMove := True;
end;

procedure TMainForm.DispMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
  procedure UpdateNb;
  begin
    nbMinX.Value := MinX;
    nbMinY.Value := MinY;
    nbMaxX.Value := MaxX;
    nbMaxY.Value := MaxY;
  end;

var
  DX, DY: Single;
begin
  if IsMove then
  begin
    DX := (X - SX) * ((MaxX - MinX)/ Disp.Width);
    DY := (Y - SY) * ((MaxY - MinY)/ Disp.Height);
    MinX := MinX - DX;
    MinY := MinY + DY;
    MaxX := MaxX - DX;
    MaxY := MaxY + DY;
    SX := X;
    SY := Y;
    UpdateNb;
    Disp.Repaint;
  end;
end;

procedure TMainForm.DispMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  IsMove := False;
end;

procedure TMainForm.DispPaint(Sender: TObject; Canvas: TCanvas);
var
  W, H, CX: Single;

  function ToDispX(X: Single): Single;
  begin
    Result := (X - MinX) * (W / (MaxX - MinX));
  end;

  function ToDispY(Y: Single): Single;
  begin
    Result := (MaxY - Y) * (H / (MaxY - MinY));
  end;

  function ToGraphX(X: Single): Single;
  begin
    Result := (X - CX) * ((MaxX - MinX) / W);
  end;

  procedure DrawFunction(Canvas: TCanvas; Color: TAlphaColor; F: TFunction);
  var
    I: Integer;
    Y, OldY: Single;
    Twice: Boolean;
  begin
    Canvas.Stroke.Color := Color;
    Twice := False;
    for I := 0 to Trunc(W) do
    begin
      OldY := Y;
      if F(ToGraphX(I), Y) then
        if Twice then
          Canvas.DrawLine(PointF(I - 1, ToDispY(OldY)), PointF(I, ToDispY(Y)), 1)
        else
          Twice := True
      else
        Twice := False;
    end;
  end;

  procedure DrawText(Canvas: TCanvas; X, Y: Single; Text: string);
  begin
    Canvas.FillText(RectF(X, Y, X + Canvas.TextWidth(Text), Y + Canvas.TextHeight(Text)), Text,
      False, 1, [], TTextAlign.Leading);
  end;

var
  t: Single;
begin
  W := Disp.Width;
  H := Disp.Height;
  CX := ToDispX(0);

  // очищаем экран
  Canvas.Fill.Color := TAlphaColorRec.White;
  Canvas.FillRect(RectF(0, 0, W, H), 0, 0, AllCorners, 1);

  // Рисуем оси
  Canvas.Stroke.Color := TAlphaColorRec.Black;
  // CX
  Canvas.DrawLine(
    PointF(0, ToDispY(0)),
    PointF(W, ToDispY(0)), 0.8);
  // CY
  Canvas.DrawLine(
    PointF(ToDispX(0), 0),
    PointF(ToDispX(0), H), 0.8);

  // Рисуем метки
  Canvas.Fill.Color := TAlphaColorRec.Mediumblue;
  DrawText(Canvas, W - 10, ToDispY(0) - 20, 'X');// CX
  DrawText(Canvas, ToDispX(0) + 10, 0, 'Y');// CY

  // Рисуем метки масштаба
  Canvas.Fill.Color := TAlphaColorRec.Fuchsia;
  DrawText(Canvas, ToDispX(DotX), ToDispY(0), FloatToStrF(DotX, TFloatFormat.ffGeneral, 0, 2));// CX
  DrawText(Canvas, ToDispX(0) + 3, ToDispY(-DotY), FloatToStrF(DotY, TFloatFormat.ffGeneral, 0, 2));// CY

  // Наносим "риски"
  // 0..MaxX
  t := DotX;
  while t <= MaxX do
  begin
    Canvas.DrawLine(PointF(ToDispX(t), ToDispY(0) - 2), PointF(ToDispX(t), ToDispY(0) + 2), 0.5);
    Canvas.DrawLine(PointF(ToDispX(t), 0), PointF(ToDispX(t), H), 0.1);
    t := t + DotX;
  end;
  // MixX..0
  t := -DotX;
  while t >= MinX do
  begin
    Canvas.DrawLine(PointF(ToDispX(t), ToDispY(0) - 2), PointF(ToDispX(t), ToDispY(0) + 2), 0.5);
    Canvas.DrawLine(PointF(ToDispX(t), 0), PointF(ToDispX(t), H), 0.1);
    t := t - DotX;
  end;
  // 0..MaxY
  t := DotY;
  while t <= MaxY do
  begin
    Canvas.DrawLine(PointF(ToDispX(0) - 2, ToDispY(t)), PointF(ToDispX(0) + 2, ToDispY(t)), 0.5);
    Canvas.DrawLine(PointF(0, ToDispY(t)), PointF(W, ToDispY(t)), 0.1);
    t := t + DotY;
  end;
  // MixY..0
  t := -DotY;
  while t >= MinY do
  begin
    Canvas.DrawLine(PointF(ToDispX(0) - 2, ToDispY(t)), PointF(ToDispX(0) + 2, ToDispY(t)), 0.5);
    Canvas.DrawLine(PointF(0, ToDispY(t)), PointF(W, ToDispY(t)), 0.1);
    t := t - DotY;
  end;

  // Рисуем графики
  DrawFunction(Canvas, TAlphaColorRec.Blue,
    function(X: Single; var Y: Single): Boolean
    begin
      Result := True;
      Y := Sin(X);
    end);

  DrawFunction(Canvas, TAlphaColorRec.Red,
    function(X: Single; var Y: Single): Boolean
    begin
      Result := X * X <= 30;
      if Result then
        Y := Sqrt(30 - X*X);
    end);

  DrawFunction(Canvas, TAlphaColorRec.Green,
    function(X: Single; var Y: Single): Boolean
    begin
      Result := Cos(X) <> 0;
      if Result then
        Y := Sin(X) / Cos(X);
    end);

  DrawFunction(Canvas, TAlphaColorRec.Orange,
    function(X: Single; var Y: Single): Boolean
    begin
      Result := X >= 0;
      if Result then
        Y := Sqrt(X) * 2;
    end);

  // Рисуем описание
  Canvas.Fill.Color := TAlphaColorRec.Blue;
  DrawText(Canvas, 2, 0, 'Y = Sin(X)');
  Canvas.Fill.Color := TAlphaColorRec.Red;
  DrawText(Canvas, 2, 16, 'Y = Sqrt(30 - X*X)');
  Canvas.Fill.Color := TAlphaColorRec.Green;
  DrawText(Canvas, 2, 32, 'Y = Sin(X) / Cos(X)');
  Canvas.Fill.Color := TAlphaColorRec.Orange;
  DrawText(Canvas, 2, 48, 'Y = Sqrt(X) * 2');
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Disp.AutoCapture := True;
  ParamsChange(nil);
end;

procedure TMainForm.ParamsChange(Sender: TObject);
begin
  if IsMove then
    Exit;
    
  MinX := nbMinX.Value;
  MinY := nbMinY.Value;
  MaxX := Max(nbMaxX.Value, MinX);
  MaxY := Max(nbMaxY.Value, MinY);
  DotX := nbDotX.Value;
  DotY := nbDotY.Value;
  Disp.Repaint;// перерисовка графика при изменении параметров

  nbMaxX.Value := MaxX;
  nbMaxY.Value := MaxY;
end;

end.

object MainForm: TMainForm
  Left = 0
  Top = 0
  Caption = 'Graphics'
  ClientHeight = 480
  ClientWidth = 652
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object Disp: TPaintBox
    Align = Client
    ClipChildren = True
    Margins.Left = 5.000000000000000000
    Margins.Top = 5.000000000000000000
    Margins.Right = 6.000000000000000000
    Margins.Bottom = 6.000000000000000000
    Size.Width = 641.000000000000000000
    Size.Height = 437.000000000000000000
    Size.PlatformDefault = False
    OnMouseDown = DispMouseDown
    OnMouseMove = DispMouseMove
    OnMouseUp = DispMouseUp
    OnPaint = DispPaint
  end
  object Panel1: TPanel
    Align = Bottom
    Position.Y = 448.000000000000000000
    Size.Width = 652.000000000000000000
    Size.Height = 32.000000000000000000
    Size.PlatformDefault = False
    StyleLookup = 'panelstyle'
    TabOrder = 1
    object nbMinX: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 2
      Cursor = crIBeam
      Min = -1000.000000000000000000
      Max = 1000.000000000000000000
      Value = -10.000000000000000000
      ValueType = Float
      Position.X = 48.000000000000000000
      Position.Y = 5.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
    object Label1: TLabel
      Position.X = 8.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Min X:'
    end
    object Label2: TLabel
      Position.X = 312.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Min Y:'
    end
    object nbMinY: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 7
      Cursor = crIBeam
      Min = -1000.000000000000000000
      Max = 1000.000000000000000000
      Value = -7.000000000000000000
      ValueType = Float
      Position.X = 352.000000000000000000
      Position.Y = 5.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
    object Label3: TLabel
      Position.X = 104.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Max X:'
    end
    object nbMaxX: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 1
      Cursor = crIBeam
      Min = -1000.000000000000000000
      Max = 1000.000000000000000000
      Value = 10.000000000000000000
      ValueType = Float
      Position.X = 144.000000000000000000
      Position.Y = 5.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
    object Label4: TLabel
      Position.X = 408.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Max Y:'
    end
    object nbMaxY: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 5
      Cursor = crIBeam
      Min = -1000.000000000000000000
      Max = 1000.000000000000000000
      Value = 7.000000000000000000
      ValueType = Float
      Position.X = 448.000000000000000000
      Position.Y = 5.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
    object Label5: TLabel
      Position.X = 200.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Dot X:'
    end
    object nbDotX: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 0
      Cursor = crIBeam
      Min = 0.009999999776482582
      Max = 1000.000000000000000000
      Value = 0.500000000000000000
      ValueType = Float
      Position.X = 240.000000000000000000
      Position.Y = 5.000000000000000000
      HorzIncrement = 0.100000001490116100
      VertIncrement = 1.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
    object Label6: TLabel
      Position.X = 504.000000000000000000
      Position.Y = 8.000000000000000000
      Size.Width = 37.000000000000000000
      Size.Height = 16.000000000000000000
      Size.PlatformDefault = False
      TextSettings.WordWrap = False
      Text = 'Dot Y:'
    end
    object nbDotY: TNumberBox
      Touch.InteractiveGestures = [LongTap, DoubleTap]
      TabOrder = 4
      Cursor = crIBeam
      Min = 0.009999999776482582
      Max = 1000.000000000000000000
      Value = 0.500000000000000000
      ValueType = Float
      Position.X = 544.000000000000000000
      Position.Y = 5.000000000000000000
      HorzIncrement = 0.100000001490116100
      VertIncrement = 1.000000000000000000
      Size.Width = 49.000000000000000000
      Size.Height = 22.000000000000000000
      Size.PlatformDefault = False
      OnChange = ParamsChange
    end
  end
end

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

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