line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Math::Fractal::Curve - Generate fractal curves |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use Math::Fractal::Curve; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# This generates a von Koch-curve. |
10
|
|
|
|
|
|
|
my $generator = [ |
11
|
|
|
|
|
|
|
[0, 0, 1/3, 0 ], |
12
|
|
|
|
|
|
|
[1/3, 0, 1/2, sqrt(5)/6], |
13
|
|
|
|
|
|
|
[1/2, sqrt(5)/6, 2/3, 0 ], |
14
|
|
|
|
|
|
|
[2/3, 0, 1, 0 ], |
15
|
|
|
|
|
|
|
]; |
16
|
|
|
|
|
|
|
# $generator may also be an anonymous subroutine that returns a |
17
|
|
|
|
|
|
|
# data structure like the above. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# New curve generator |
20
|
|
|
|
|
|
|
my $curve_gen = Math::Fractal::Curve->new(generator => $generator); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# New curve |
23
|
|
|
|
|
|
|
my $curve = $curve_gen->line( |
24
|
|
|
|
|
|
|
start => [-2, 1], |
25
|
|
|
|
|
|
|
end => [2, -1], |
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $edges = $curve->fractal($depth); |
29
|
|
|
|
|
|
|
# (now containing array ref of array refs of x1,y1,x2,y2 coordinates) |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 DESCRIPTION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
This module is intended to generate 2-dimensional fractal curves such as |
34
|
|
|
|
|
|
|
the von Koch curve from simple generator functions. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
The fractals are generated by recursively replacing a distance with the |
37
|
|
|
|
|
|
|
generator. Hence, the starting distance and the generator define such a |
38
|
|
|
|
|
|
|
fractal curve. Generators describe what a given distance is going to be |
39
|
|
|
|
|
|
|
replaced with in terms of lengths of the distance. For example, |
40
|
|
|
|
|
|
|
a generator of ([0, 0, 1/3, 0], [2/3, 0, 1, 0]) describes a |
41
|
|
|
|
|
|
|
Mid-third Cantor Set which means the the middle third of every distance |
42
|
|
|
|
|
|
|
in the set is deleted. Syntax for generator data structures in the context |
43
|
|
|
|
|
|
|
of this module is [[x1, y1, x2, y2], [X1, Y1, X2, Y2]] (array ref of array |
44
|
|
|
|
|
|
|
refs of edge coordinates) where xn,yn are the two coordinate pairs |
45
|
|
|
|
|
|
|
specifying the first edge a distance is to be replaced with |
46
|
|
|
|
|
|
|
and Xn,Yn are the second edge. There may be any number of edges. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
For more telling examples, please have a thorough look at the examples |
49
|
|
|
|
|
|
|
subdirectory that came with this distribution or look through the examples |
50
|
|
|
|
|
|
|
page of this module on |
51
|
|
|
|
|
|
|
http://steffen-mueller.net/modules/Math-Fractal-Curve/examples |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Furthermore, the generator may be either one of the aformentioned nested |
54
|
|
|
|
|
|
|
array references, or it may be an anonymous subroutine that returns such |
55
|
|
|
|
|
|
|
a data structure. This enables you to generate I fractal curves |
56
|
|
|
|
|
|
|
or fractal curves whose trajectories depend on the distance any |
57
|
|
|
|
|
|
|
generator is to replace, etc. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
While the above feature makes the probablistic / dynamic curves non-fractal, |
60
|
|
|
|
|
|
|
they preserve some properties real fractals have. Please refer to the |
61
|
|
|
|
|
|
|
literature mentioned under L for more information. The examples |
62
|
|
|
|
|
|
|
subdirectory of the distribution also holds an example of a probalistic |
63
|
|
|
|
|
|
|
von Koch-curve and a Koch curve whose excavation-direction (the direction |
64
|
|
|
|
|
|
|
the triangle points at) depends on the orientation of the distance the |
65
|
|
|
|
|
|
|
generator is applied to (spatial.pl). |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Generator subroutines are passed the curve object as first argument. They |
68
|
|
|
|
|
|
|
may access any attributes of the curve segment they are applied to, but |
69
|
|
|
|
|
|
|
most interestingly, they may access their {start} and {end} attributes that |
70
|
|
|
|
|
|
|
hold array references [x,y] of the start- and end points of the distance |
71
|
|
|
|
|
|
|
they are being applied to. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 EXPORT |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
None. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 METHODS |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
package Math::Fractal::Curve; |
82
|
|
|
|
|
|
|
|
83
|
1
|
|
|
1
|
|
663
|
use 5.006; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
30
|
|
84
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
24
|
|
85
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
25
|
|
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
1
|
|
3
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
694
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
our $VERSION = '1.03'; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head2 Constructor new |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
The new() constructor requires one named argument: |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
generator => GENERATOR |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
where GENERATOR may either be a generator-datastructure as described |
99
|
|
|
|
|
|
|
earlier or a subroutine reference (or closure) that returns such a |
100
|
|
|
|
|
|
|
data structure. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Furthermore, new accepts any key/value pairs that will be made attributes |
103
|
|
|
|
|
|
|
of the curve object. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
new() is both a class- and an object method and thus can be used to clone |
106
|
|
|
|
|
|
|
existing curves. (And is internally used to do so.) |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub new { |
111
|
4
|
|
|
4
|
1
|
767
|
my $proto = shift; |
112
|
4
|
|
66
|
|
|
16
|
my $class = ref($proto) || $proto; |
113
|
|
|
|
|
|
|
|
114
|
4
|
|
|
|
|
5
|
my $self = {}; |
115
|
4
|
100
|
|
|
|
11
|
if (ref $proto) { |
116
|
3
|
|
|
|
|
6
|
$self->{generator} = $proto->{generator}; |
117
|
3
|
100
|
66
|
|
|
21
|
if (exists $proto->{end} and exists $proto->{start}) { |
118
|
2
|
|
|
|
|
3
|
$self->{end} = [@{$proto->{end}}]; |
|
2
|
|
|
|
|
6
|
|
119
|
2
|
|
|
|
|
3
|
$self->{start} = [@{$proto->{start}}]; |
|
2
|
|
|
|
|
16
|
|
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
} |
122
|
4
|
|
|
|
|
15
|
for (my $i = 0; $i < @_; $i+=2) { |
123
|
7
|
|
|
|
|
24
|
$self->{$_[$i]} = $_[$i+1]; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
4
|
|
|
|
|
5
|
delete $self->{_edges}; |
127
|
4
|
|
|
|
|
10
|
bless $self => $class; |
128
|
|
|
|
|
|
|
|
129
|
4
|
50
|
|
|
|
10
|
if (not exists $self->{generator}) { |
130
|
0
|
|
|
|
|
0
|
croak "You need to supply a generator subroutine."; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
4
|
|
|
|
|
10
|
return $self; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head2 Method line |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
The line() method takes two required named arguments: |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
start => [START_X, START_Y], |
143
|
|
|
|
|
|
|
end => [END_X, END_Y ] |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
where START_X, START_Y and END_X, END_Y are the coordinates of the |
146
|
|
|
|
|
|
|
start- and end points of the distance to create the fractal curve from. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
line() stores this data in the {start} and {end} attributes of the |
149
|
|
|
|
|
|
|
curve object. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=cut |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub line { |
154
|
1
|
|
|
1
|
1
|
355
|
my $self = shift; |
155
|
1
|
|
|
|
|
4
|
my %args = @_; |
156
|
1
|
|
|
|
|
1
|
my $start = $args{start}; |
157
|
1
|
|
|
|
|
2
|
my $end = $args{end}; |
158
|
|
|
|
|
|
|
|
159
|
1
|
50
|
33
|
|
|
6
|
if (not defined $start or not defined $end) { |
160
|
0
|
|
|
|
|
0
|
croak "You need to supply start- and end point."; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
1
|
|
|
|
|
3
|
$self = $self->new(start => $start, end => $end); |
164
|
1
|
|
|
|
|
4
|
return $self; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head2 Method recurse() |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
The recurse() method applies the generator to the curve's distance |
172
|
|
|
|
|
|
|
and returns a reference to an array of new curve objects that represent |
173
|
|
|
|
|
|
|
the newly generated edges. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=cut |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub recurse { |
178
|
2
|
|
|
2
|
1
|
347
|
my $self = shift; |
179
|
2
|
|
|
|
|
5
|
my $edges = $self->edges(); |
180
|
|
|
|
|
|
|
|
181
|
2
|
|
|
|
|
3
|
my $obj = []; |
182
|
2
|
|
|
|
|
4
|
foreach my $e (@$edges) { |
183
|
2
|
|
|
|
|
7
|
push @$obj, $self->new( |
184
|
|
|
|
|
|
|
start => [$e->[0], $e->[1]], |
185
|
|
|
|
|
|
|
end => [$e->[2], $e->[3]], |
186
|
|
|
|
|
|
|
); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
2
|
|
|
|
|
7
|
return $obj; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 Method fractal() |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
The fractal() method takes one argument: The recursion depth of the |
197
|
|
|
|
|
|
|
discrete fractal representation. Obviously, the complexity is |
198
|
|
|
|
|
|
|
Edges^Depth with Edges equal to the number of edges of the generator. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
fractal() returns a reference to an array of array references. These |
201
|
|
|
|
|
|
|
referenced arrays contain (x1, y1, x2, y2) coordinates of edges. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=cut |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub fractal { |
206
|
3
|
|
|
3
|
1
|
1012
|
my $self = shift; |
207
|
3
|
|
|
|
|
3
|
my $depth = shift; |
208
|
|
|
|
|
|
|
|
209
|
3
|
50
|
|
|
|
8
|
croak "First argument must be recursion depth!" unless defined $depth; |
210
|
|
|
|
|
|
|
|
211
|
3
|
100
|
|
|
|
6
|
return [[@{$self->{start}}, @{$self->{end}}]] if $depth <= 0; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
3
|
|
212
|
|
|
|
|
|
|
|
213
|
2
|
|
|
|
|
2
|
$depth--; |
214
|
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
4
|
my $result = [$self]; |
216
|
2
|
|
|
|
|
4
|
foreach (1..$depth) { |
217
|
1
|
|
|
|
|
2
|
my $temp = []; |
218
|
1
|
|
|
|
|
2
|
foreach (@$result) { |
219
|
1
|
|
|
|
|
2
|
push @$temp, @{$_->recurse()}; |
|
1
|
|
|
|
|
2
|
|
220
|
|
|
|
|
|
|
} |
221
|
1
|
|
|
|
|
2
|
$result = $temp; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
2
|
|
|
|
|
4
|
@$result = map {@{$_->edges()}} @$result; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
3
|
|
225
|
|
|
|
|
|
|
|
226
|
2
|
|
|
|
|
4
|
return $result; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=head2 Method edges() |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
The edges() method returns a reference to an array of array references. |
234
|
|
|
|
|
|
|
These referenced arrays contain (x1, y1, x2, y2) coordinates of the |
235
|
|
|
|
|
|
|
edges that are generated by the generator from the curve's starting |
236
|
|
|
|
|
|
|
edge. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub edges { |
241
|
5
|
|
|
5
|
1
|
346
|
my $self = shift; |
242
|
|
|
|
|
|
|
|
243
|
5
|
100
|
|
|
|
16
|
return $self->{_edges} if exists $self->{_edges}; |
244
|
2
|
|
|
|
|
4
|
my $edges; |
245
|
2
|
50
|
|
|
|
6
|
if (ref $self->{generator} eq 'CODE') { |
246
|
0
|
|
|
|
|
0
|
$edges = $self->{generator}->($self) |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
else { |
249
|
2
|
|
|
|
|
3
|
$edges = $self->{generator}; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
2
|
|
|
|
|
3
|
my $start = $self->{start}; |
253
|
2
|
|
|
|
|
2
|
my $end = $self->{end}; |
254
|
|
|
|
|
|
|
|
255
|
2
|
|
|
|
|
5
|
my $vec = [ |
256
|
|
|
|
|
|
|
$end->[0] - $start->[0], |
257
|
|
|
|
|
|
|
$end->[1] - $start->[1], |
258
|
|
|
|
|
|
|
]; |
259
|
2
|
|
|
|
|
12
|
my $len = sqrt( |
260
|
|
|
|
|
|
|
$vec->[0]**2 + |
261
|
|
|
|
|
|
|
$vec->[1]**2 |
262
|
|
|
|
|
|
|
); |
263
|
|
|
|
|
|
|
|
264
|
2
|
|
|
|
|
3
|
my $sin = $vec->[1]/$len; |
265
|
2
|
|
|
|
|
3
|
my $cos = $vec->[0]/$len; |
266
|
|
|
|
|
|
|
|
267
|
2
|
|
|
|
|
3
|
my $edges_res = []; |
268
|
2
|
|
|
|
|
3
|
foreach my $e (@$edges) { |
269
|
2
|
|
|
|
|
7
|
my ($x1, $y1, $x2, $y2) = map $_*$len, @$e; |
270
|
|
|
|
|
|
|
|
271
|
2
|
|
|
|
|
11
|
push @$edges_res, [ |
272
|
|
|
|
|
|
|
$start->[0] + $x1*$cos - $y1*$sin, |
273
|
|
|
|
|
|
|
$start->[1] + $x1*$sin + $y1*$cos, |
274
|
|
|
|
|
|
|
$start->[0] + $x2*$cos - $y2*$sin, |
275
|
|
|
|
|
|
|
$start->[1] + $x2*$sin + $y2*$cos |
276
|
|
|
|
|
|
|
]; |
277
|
|
|
|
|
|
|
} |
278
|
2
|
|
|
|
|
4
|
$self->{_edges} = $edges_res; |
279
|
2
|
|
|
|
|
8
|
return $edges_res; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
1; |
284
|
|
|
|
|
|
|
__END__ |