-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathutransform.pas
150 lines (122 loc) · 3.22 KB
/
utransform.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
unit UTransform;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
type
{ TDoublePoint }
TDoublePoint = record
x, y: double;
end;
PDoublePoint = ^TDoublePoint;
function DoublePoint(AX, AY: double): TDoublePoint;
operator <> (p1, p2: TDoublePoint): boolean;
function Dist(P1, P2: TPoint): double; overload; inline;
function Dist(P1, P2: TDoublePoint): double; overload; inline;
function CanvasToWorld(AX, AY: integer): TDoublePoint;
function CanvasToWorld(APoint: TPoint): TDoublePoint;
function WorldToCanvas(AX, AY: double): TPoint;
function WorldToCanvas(ADoublePoint: TDoublePoint): TPoint;
procedure SetScale(AScale: double);
procedure ZoomPoint(APoint: TDoublePoint; AScale: double);
procedure ZoomRect(ATopLeft, ABottomRight: TDoublePoint; AScale: double);
procedure CenterToPoint(APoint: TDoublePoint);
const
ZOOM_MIN = 0.01;
ZOOM_MAX = 32.00;
var
Scale: double;
CanvasOffset: TDoublePoint;
CanvasWidth, CanvasHeight: integer;
implementation
function DoublePoint(AX, AY: double): TDoublePoint;
begin
with Result do
begin
x := AX;
y := AY;
end;
end;
operator<>(p1, p2: TDoublePoint): boolean;
begin
Result := (p1.x <> p2.x) or (p1.y <> p2.y);
end;
function Dist(P1, P2: TPoint): double; inline;
begin
Result := sqrt(sqr(P2.x - P1.x) + sqr(P2.y - P1.y));
end;
function Dist(P1, P2: TDoublePoint): double; inline;
begin
Result := sqrt(sqr(P2.x - P1.x) + sqr(P2.y - P1.y));
end;
function CanvasToWorld(AX, AY: integer): TDoublePoint;
begin
with Result do
begin
x := AX / Scale + CanvasOffset.x;
y := AY / Scale + CanvasOffset.y;
end;
end;
function CanvasToWorld(APoint: TPoint): TDoublePoint;
begin
Result := CanvasToWorld(APoint.x, APoint.y);
end;
function WorldToCanvas(AX, AY: double): TPoint;
begin
with Result do
begin
x := Round((AX - CanvasOffset.x) * Scale);
y := Round((AY - CanvasOffset.y) * Scale);
end;
end;
function WorldToCanvas(ADoublePoint: TDoublePoint): TPoint;
begin
Result := WorldToCanvas(ADoublePoint.x, ADoublePoint.y);
end;
procedure SetScale(AScale: double);
begin
if AScale < ZOOM_MIN then
Scale := ZOOM_MIN
else if AScale > ZOOM_MAX then
Scale := ZOOM_MAX
else
Scale := AScale;
end;
procedure ZoomPoint(APoint: TDoublePoint; AScale: double);
var
PrevScale: double;
CanvasPos: TPoint;
begin
CanvasPos := WorldToCanvas(APoint);
PrevScale := Scale;
SetScale(AScale);
if Scale = PrevScale then
exit;
CanvasOffset.x := APoint.x - (CanvasPos.x / Scale);
CanvasOffset.y := APoint.y - (CanvasPos.y / Scale);
end;
procedure ZoomRect(ATopLeft, ABottomRight: TDoublePoint; AScale: double);
var
PrevScale: double;
RectCenter: TDoublePoint;
begin
RectCenter := DoublePoint((ABottomRight.x + ATopLeft.x) / 2,
(ABottomRight.y + ATopLeft.y) / 2);
PrevScale := Scale;
SetScale(AScale);
if Scale = PrevScale then
exit;
CenterToPoint(RectCenter);
end;
procedure CenterToPoint(APoint: TDoublePoint);
var
CanvasCorner: TDoublePoint;
begin
CanvasCorner := CanvasToWorld(CanvasWidth, CanvasHeight);
CanvasOffset.x := APoint.x - (CanvasCorner.x - CanvasOffset.x) / 2;
CanvasOffset.y := APoint.y - (CanvasCorner.y - CanvasOffset.y) / 2;
end;
initialization
CanvasOffset := DoublePoint(0, 0);
Scale := 1.0;
end.