File Coverage

blib/lib/Graphics/GVG/OpenGLRenderer.pm
Criterion Covered Total %
statement 24 173 13.8
branch 0 26 0.0
condition n/a
subroutine 8 24 33.3
pod 2 2 100.0
total 34 225 15.1


line stmt bran cond sub pod time code
1             # Copyright (c) 2016 Timm Murray
2             # All rights reserved.
3             #
4             # Redistribution and use in source and binary forms, with or without
5             # modification, are permitted provided that the following conditions are met:
6             #
7             # * Redistributions of source code must retain the above copyright notice,
8             # this list of conditions and the following disclaimer.
9             # * Redistributions in binary form must reproduce the above copyright
10             # notice, this list of conditions and the following disclaimer in the
11             # documentation and/or other materials provided with the distribution.
12             #
13             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23             # POSSIBILITY OF SUCH DAMAGE.
24             package Graphics::GVG::OpenGLRenderer;
25             $Graphics::GVG::OpenGLRenderer::VERSION = '0.2';
26             # ABSTRACT: Turn a GVG file into OpenGL code
27 1     1   638 use strict;
  1         1  
  1         23  
28 1     1   2 use warnings;
  1         1  
  1         19  
29 1     1   491 use Moose;
  1         284841  
  1         6  
30 1     1   5336 use namespace::autoclean;
  1         5311  
  1         4  
31 1     1   420 use Data::UUID;
  1         495  
  1         50  
32 1     1   444 use Imager::Color;
  1         31680  
  1         33  
33 1     1   583 use Math::Trig 'pi';
  1         8848  
  1         1336  
34              
35             has [qw{ circle_segments ellipse_segments }] => (
36             is => 'rw',
37             isa => 'Int',
38             default => 40,
39             );
40             has '_glow_count' => (
41             traits => ['Counter'],
42             is => 'ro',
43             isa => 'Int',
44             default => 0,
45             handles => {
46             '_increment_glow' => 'inc',
47             '_decrement_glow' => 'dec',
48             },
49             );
50              
51              
52             sub make_drawer_obj
53             {
54 0     0 1   my ($self, $ast) = @_;
55              
56 0           my ($code, $drawer_pack) = $self->make_code( $ast );
57 0 0         eval $code or die $@;
58              
59 0           my $obj = $drawer_pack->new;
60 0           return $obj;
61             }
62              
63             sub make_code
64             {
65 0     0 1   my ($self, $ast) = @_;
66 0           my $drawer_pack = $self->_make_pack;
67 0           my $code = $self->_make_pack_code( $drawer_pack, $ast );
68 0           return ($code, $drawer_pack);
69             }
70              
71             sub _make_pack
72             {
73 0     0     my ($self) = @_;
74 0           my $uuid = Data::UUID->new->create_hex;
75 0           my $pack = __PACKAGE__ . '::' . $uuid;
76 0           return $pack;
77             }
78              
79             sub _make_pack_code
80             {
81 0     0     my ($self, $pack, $ast) = @_;
82              
83 0           my $code = 'package ' . $pack . ';';
84 0           $code .= q!
85             use strict;
86             use warnings;
87             use OpenGL qw(:all);
88             !;
89 0           $code .= q!
90             sub new
91             {
92             my ($class) = @_;
93             my $self = {};
94             bless $self => $class;
95             return $self;
96             }
97             !;
98              
99 0           $code .= 'sub draw {';
100 0           $code .= $self->_make_draw_code( $ast );
101 0           $code .= 'return; }';
102              
103 0           $code .= '1;';
104 0           return $code;
105             }
106              
107             sub _make_draw_code
108             {
109 0     0     my ($self, $ast) = @_;
110             my $code = join( "\n", map {
111 0           my $ret = '';
112 0 0         if(! ref $_ ) {
    0          
    0          
    0          
    0          
    0          
    0          
113 0           warn "Not a ref, don't know what to do with '$_'\n";
114             }
115             elsif( $_->isa( 'Graphics::GVG::AST::Line' ) ) {
116 0           $ret = $self->_make_code_line( $_ );
117             }
118             elsif( $_->isa( 'Graphics::GVG::AST::Rect' ) ) {
119 0           $ret = $self->_make_code_rect( $_ );
120             }
121             elsif( $_->isa( 'Graphics::GVG::AST::Polygon' ) ) {
122 0           $ret = $self->_make_code_poly( $_ );
123             }
124             elsif( $_->isa( 'Graphics::GVG::AST::Circle' ) ) {
125 0           $ret = $self->_make_code_circle( $_ );
126             }
127             elsif( $_->isa( 'Graphics::GVG::AST::Ellipse' ) ) {
128 0           $ret = $self->_make_code_ellipse( $_ );
129             }
130             elsif( $_->isa( 'Graphics::GVG::AST::Glow' ) ) {
131 0           $self->_increment_glow;
132 0           $ret = $self->_make_draw_code( $_ );
133 0           $self->_decrement_glow;
134             }
135             else {
136 0           warn "Don't know what to do with " . ref($_) . "\n";
137             }
138              
139 0           $ret;
140 0           } @{ $ast->commands });
  0            
141 0           return $code;
142             }
143              
144             sub _make_code_line
145             {
146 0     0     my ($self, $cmd) = @_;
147 0           my $x1 = $cmd->x1;
148 0           my $y1 = $cmd->y1;
149 0           my $x2 = $cmd->x2;
150 0           my $y2 = $cmd->y2;
151 0           my $color = $cmd->color;
152 0           my ($red, $green, $blue, $alpha) = $self->_int_to_opengl_color( $color );
153              
154             my $make_line_sub = sub {
155 0     0     my ($width, $red, $green, $blue, $alpha) = @_;
156 0           my $code = qq!
157             glLineWidth( $width );
158             glColor4ub( $red, $green, $blue, $alpha );
159             glBegin( GL_LINES );
160             glVertex2f( $x1, $y1 );
161             glVertex2f( $x2, $y2 );
162             glEnd();
163             !;
164 0           return $code;
165 0           };
166              
167 0           my $code = '';
168 0 0         if( $self->_glow_count > 0 ) {
169             # TODO not really getting the effect I was hoping for. Play around
170             # with it later.
171 0           my @colors1 = $self->_brighten( 2.0, $red, $green, $blue, $alpha );
172 0           my @colors2 = ($red, $green, $blue, $alpha);
173 0           my @colors3 = $self->_brighten( 0.7, $red, $green, $blue, $alpha );
174 0           $code = $make_line_sub->( 5.0, @colors3 );
175 0           $code .= $make_line_sub->( 2.0, @colors2 );
176             #$code .= $make_line_sub->( 1.0, @colors1 );
177             }
178             else {
179 0           $code = $make_line_sub->( 1.0, $red, $green, $blue, $alpha );
180             }
181              
182 0           return $code;
183             }
184              
185             sub _make_code_rect
186             {
187 0     0     my ($self, $cmd) = @_;
188 0           my $x = $cmd->x;
189 0           my $y = $cmd->y;
190 0           my $width = $cmd->width;
191 0           my $height = $cmd->height;
192 0           my $color = $cmd->color;
193 0           my ($red, $green, $blue, $alpha) = $self->_int_to_opengl_color( $color );
194              
195             my $make_rect_sub = sub {
196 0     0     my ($width, $red, $green, $blue, $alpha) = @_;
197 0           my $far_x = $x + $width;
198 0           my $far_y = $y + $height;
199 0           my $code = qq!
200             glLineWidth( $width );
201             glColor4ub( $red, $green, $blue, $alpha );
202             glBegin( GL_LINES );
203             glVertex2f( $x, $y );
204             glVertex2f( $far_x, $y );
205              
206             glVertex2f( $far_x, $y );
207             glVertex2f( $far_x, $far_y );
208              
209             glVertex2f( $far_x, $far_y );
210             glVertex2f( $x, $far_y );
211              
212             glVertex2f( $x, $far_y );
213             glVertex2f( $x, $y );
214             glEnd();
215             !;
216 0           return $code;
217 0           };
218              
219 0           my $code = '';
220 0 0         if( $self->_glow_count > 0 ) {
221             # TODO
222 0           $code = $make_rect_sub->( 1.0, $red, $green, $blue, $alpha );
223             }
224             else {
225 0           $code = $make_rect_sub->( 1.0, $red, $green, $blue, $alpha );
226             }
227              
228 0           return $code;
229             }
230              
231             sub _make_code_circle
232             {
233 0     0     my ($self, $cmd) = @_;
234 0           my $cx = $cmd->cx;
235 0           my $cy = $cmd->cy;
236 0           my $r = $cmd->r;
237 0           my $color = $cmd->color;
238              
239 0           my $poly = Graphics::GVG::AST::Polygon->new({
240             cx => $cx,
241             cy => $cy,
242             r => $r,
243             sides => $self->circle_segments,
244             rotate => 0,
245             color => $cmd->color,
246             });
247              
248 0           return $self->_make_code_poly( $poly );
249             }
250              
251             sub _make_code_ellipse
252             {
253 0     0     my ($self, $cmd) = @_;
254 0           my $cx = $cmd->cx;
255 0           my $cy = $cmd->cy;
256 0           my $rx = $cmd->rx;
257 0           my $ry = $cmd->ry;
258 0           my $color = $cmd->color;
259 0           my $num_segments = $self->ellipse_segments;
260 0           my ($red, $green, $blue, $alpha) = $self->_int_to_opengl_color( $color );
261              
262             # See:
263             # http://stackoverflow.com/questions/5886628/effecient-way-to-draw-ellipse-with-opengl-or-d3d
264             my $make_cmd_sub = sub {
265 0     0     my ($width, $red, $green, $blue, $alpha) = @_;
266 0           my $theta = 2 * pi / $num_segments;
267 0           my $c = cos( $theta );
268 0           my $s = sin( $theta );
269 0           my $t;
270              
271 0           my $x = 1;
272 0           my $y = 0;
273              
274 0           my $code = qq!
275             glLineWidth( $width );
276             glColor4ub( $red, $green, $blue, $alpha );
277             glBegin(GL_LINE_LOOP);
278             !;
279 0           foreach my $i (0 .. $num_segments) {
280 0           my $set_x = $x * $rx + $cx;
281 0           my $set_y = $y * $ry + $cy;
282 0           $code .= qq!
283             glVertex2f( $set_x, $set_y );
284             !;
285              
286 0           $t = $x;
287 0           $x = $c * $x - $s * $y;
288 0           $y = $s * $t + $c * $y;
289             }
290              
291 0           $code .= q!
292             glEnd();
293             !;
294 0           return $code;
295 0           };
296              
297 0           my $code = '';
298 0 0         if( $self->_glow_count > 0 ) {
299             # TODO
300 0           $code = $make_cmd_sub->( 1.0, $red, $green, $blue, $alpha );
301             }
302             else {
303 0           $code = $make_cmd_sub->( 1.0, $red, $green, $blue, $alpha );
304             }
305 0           return $code;
306             }
307              
308             sub _make_code_poly
309             {
310 0     0     my ($self, $cmd) = @_;
311 0           my @coords = @{ $cmd->coords };
  0            
312 0           my $color = $cmd->color;
313 0           my ($red, $green, $blue, $alpha) = $self->_int_to_opengl_color( $color );
314              
315             my $make_code_sub = sub {
316 0     0     my ($width, $red, $green, $blue, $alpha) = @_;
317 0           my $code = qq!
318             glLineWidth( $width );
319             glColor4ub( $red, $green, $blue, $alpha );
320             glBegin( GL_LINES );
321             !;
322              
323 0           foreach my $i (0 .. $#coords - 1) {
324 0           my $x1 = $coords[$i][0];
325 0           my $y1 = $coords[$i][1];
326 0           my $x2 = $coords[$i+1][0];
327 0           my $y2 = $coords[$i+1][1];
328              
329 0           $code .= qq!
330             glVertex2f( $x1, $y1 );
331             glVertex2f( $x2, $y2 );
332             !;
333             }
334 0           $code .= qq!
335             glVertex2f( $coords[-1][0], $coords[-1][1] );
336             glVertex2f( $coords[0][0], $coords[0][1] );
337             glEnd();
338             !;
339 0           return $code;
340 0           };
341              
342 0           my $code = '';
343 0 0         if( $self->_glow_count > 0 ) {
344             # TODO
345 0           $code = $make_code_sub->( 1.0, $red, $green, $blue, $alpha );
346             }
347             else {
348 0           $code = $make_code_sub->( 1.0, $red, $green, $blue, $alpha );
349             }
350              
351 0           return $code;
352             }
353              
354             sub _int_to_opengl_color
355             {
356 0     0     my ($self, $color) = @_;
357 0           my $red = ($color >> 24) & 0xFF;
358 0           my $green = ($color >> 16) & 0xFF;
359 0           my $blue = ($color >> 8) & 0xFF;
360 0           my $alpha = $color & 0xFF;
361 0           return ($red, $green, $blue, $alpha);
362             }
363              
364             sub _brighten
365             {
366 0     0     my ($self, $multiplier, $red, $green, $blue, $alpha) = @_;
367 0           my $color = Imager::Color->new( $red, $green, $blue, $alpha );
368 0           my ($h, $s, $v, $new_alpha) = $color->hsv;
369              
370 0           $v *= $multiplier;
371 0 0         $v = 1.0 if $v > 1.0;
372              
373 0           my $hsv_color = Imager::Color->new(
374             hue => $h,
375             v => $v,
376             s => $s,
377             alpha => $new_alpha,
378             );
379 0           return $hsv_color->rgba;
380             }
381              
382              
383 1     1   8 no Moose;
  1         1  
  1         13  
384             __PACKAGE__->meta->make_immutable;
385             1;
386             __END__
387              
388              
389             =head1 NAME
390              
391             Graphics::GVG::OpenGLRenderer - Take a GVG file and turn it into Perl/OpenGL code
392              
393             =head1 DESCRIPTION
394              
395             =head1 ATTRIBUTES
396              
397             =head2 circle_segments / ellipse_segments
398              
399             In OpenGL, circles aren't really circles. They're polygons with a large number
400             of sides, which blur together enough to look like a circle.
401              
402             These attributes control how many sides those polygons will have. A circle or
403             ellipse that appears larger on the screen will need to be rendered with a
404             larger number of sides to maintain the illusion.
405              
406             The default is 40 for both.
407              
408             =head1 METHODS
409              
410             =head2 make_drawer_obj
411              
412             my $opengl = $renderer->make_drawer_obj( $ast );
413              
414             Given an L<Graphics::GVG::AST> object, generates a new Perl object that, when
415             you call its C<draw()> method, will output the GVG description to OpenGL.
416              
417             The package will be uniquely created under C<Graphics::GVG::OpenGLRenderer>.
418              
419             =head2 make_code
420              
421             my $pack_code = $renderer->make_code( $ast );
422              
423             Given an L<Graphics::GVG::AST> object, returns the code that can be used to
424             create the same kind of Perl object made by C<make_drawer_obj()>.
425              
426             =head1 LICENSE
427              
428             Copyright (c) 2016 Timm Murray
429             All rights reserved.
430              
431             Redistribution and use in source and binary forms, with or without
432             modification, are permitted provided that the following conditions are met:
433              
434             * Redistributions of source code must retain the above copyright notice,
435             this list of conditions and the following disclaimer.
436             * Redistributions in binary form must reproduce the above copyright
437             notice, this list of conditions and the following disclaimer in the
438             documentation and/or other materials provided with the distribution.
439              
440             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
441             AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
442             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
443             ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
444             LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
445             CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
446             SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
447             INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
448             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
449             ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
450             POSSIBILITY OF SUCH DAMAGE.
451              
452             =cut