CAD-Drawing
view release on metacpan or search on metacpan
lib/CAD/Drawing/Manipulate/Graphics.pm view on Meta::CPAN
package CAD::Drawing::Manipulate::Graphics;
our $VERSION = '0.02';
use CAD::Drawing;
use CAD::Drawing::Defined;
use Image::Magick;
push(@CAD::Drawing::ISA, __PACKAGE__);
use warnings;
use strict;
use Carp;
=pod
=head1 Name
CAD::Drawing::Manipulate::Graphics - Gimp meets CAD?
=head1 AUTHOR
Eric L. Wilhelm <ewilhelm at cpan dot org>
http://scratchcomputing.com
=head1 COPYRIGHT
This module is copyright (C) 2004-2006 by Eric L. Wilhelm. Portions
copyright (C) 2003 by Eric L. Wilhelm and A. Zahner Co.
=head1 LICENSE
This module is distributed under the same terms as Perl. See the Perl
source package for details.
You may use this software under one of the following licenses:
(1) GNU General Public License
(found at http://www.gnu.org/copyleft/gpl.html)
(2) Artistic License
(found at http://www.perl.com/pub/language/misc/Artistic.html)
=head1 NO WARRANTY
This software is distributed with ABSOLUTELY NO WARRANTY. The author,
his former employer, and any other contributors will in no way be held
liable for any loss or damages resulting from its use.
=head1 Modifications
The source code of this module is made freely available and
distributable under the GPL or Artistic License. Modifications to and
use of this software must adhere to one of these licenses. Changes to
the code should be noted as such and this notification (as well as the
above copyright information) must remain intact on all copies of the
code.
Additionally, while the author is actively developing this code,
notification of any intended changes or extensions would be most helpful
in avoiding repeated work for all parties involved. Please contact the
author with any such development plans.
=cut
########################################################################
=head1 Methods
All of these are CAD::Drawing methods (I force my own inheritance:)
=cut
########################################################################
=head2 image_init
Initialize the image at $addr based on the value at the fullpath key.
This establishes the contained Image::Magick object and loads the image
into memory in the image_handle key.
$drw->image_init($addr);
=cut
sub image_init {
my $self = shift;
my ($addr) = @_;
($addr->{type} eq "images") or croak("item is not an image\n");
my $obj = $self->getobj($addr);
my $name = $obj->{fullpath};
(-e $name) or croak("$name does not exist\n");
# print "loading $name ...\n";
my $im = Image::Magick->new();
my $err = $im->Read($name);
$err && carp("read $name gave $err\n");
$obj->{image_handle} = $im;
} # end subroutine image_init definition
########################################################################
=head2 image_crop
Crops an image and its definition (actually, changes its insert point)
according to the points given by @crop_points (which maybe had better be
within the object (but I don't really sweat that.))
@crop_points should be in world coordinates as follows:
@crop_points = (
[$lower_left_x , $lower_left_y ],
[$upper_right_x, $upper_right_y],
);
# note that you can get these as
# ($drw->getExtentsRec($something))[0,2]
$drw->image_crop($addr, \@crop_points);
=cut
sub image_crop {
my $dbg = 0;
my $self = shift;
my ($addr, $crp_pts) = @_;
($addr->{type} eq "images") or croak("not an image\n");
my $obj = $self->getobj($addr);
(ref($crp_pts) eq "ARRAY") or croak("$crp_pts is not array\n");
(@$crp_pts == 2) or croak("crop points should be 2\n");
# need upper left first
my @crop_start = map({sprintf("%0.0f", $_)}
$self->drw_to_img(
[
$crp_pts->[0][0], # leftmost x
$crp_pts->[1][1] # uppermost y
],
$addr)
);
my @crop_stop = map({sprintf("%0.0f", $_)}
$self->drw_to_img(
[
$crp_pts->[1][0], # rightmost x
$crp_pts->[0][1] # lowermost y
],
$addr)
);
my @ext = map({$crop_stop[$_] - $crop_start[$_]} 0,1);
my $im = $obj->{image_handle};
my @old_ext = $self->get_world_image_rectangle($addr);
$dbg && print "old extents @{$obj->{size}}\n";
$dbg && print "new extents: @ext\n";
$dbg && print "start crop: @crop_start\n";
$dbg && print "stop crop: @crop_stop\n";
$im->Crop(
width => $ext[0], height => $ext[1],
x => $crop_start[0], y => $crop_start[1],
);
my @sz = $im->Get("width", "height");
$dbg && print "check: @sz\n";
# image processing does strange things, so we use the size reported
# by Image::Magick to reset the insert point and size of the image
my @new_base = (
$crop_start[0],
$crop_start[1] + $sz[1],
);
my @new_pt = $self->img_to_drw(\@new_base, $addr);
$dbg && print "old insert: @{$obj->{pt}}\n";
$dbg && print "new basepoint: @new_base at @new_pt\n";
$obj->{pt} = [@new_pt];
$obj->{size} = [@sz];
if(0) {
my $check = CAD::Drawing->new();
$check->addpolygon(\@old_ext);
$check->addrec($crp_pts, {color => "blue"});
$check->addpolygon(
[$self->get_world_image_rectangle($addr)], {color => "red"}
);
$check->show(hang=>1);
exit;
}
} # end subroutine image_crop definition
########################################################################
=head2 image_scale
Scales both the image and the definition by $scale, starting at
@base_point.
$drw->image_scale($addr, $scale, \@base_point);
=cut
sub image_scale {
my $self = shift;
my ($addr, $scale, $point) = @_;
($addr->{type} eq "images") or croak("not an image\n");
# this sets only the insert:
$self->Scale($addr, $scale, $point);
# maybe not scale image here (punt like autoheck)
my $obj = $self->getobj($addr);
# really should put this in the manipulate code?
$obj->{vector}[0][0] *=$scale;
$obj->{vector}[1][1] *=$scale;
print "vectors now $obj->{vector}[0][0], $obj->{vector}[1][1]\n";
} # end subroutine image_scale definition
########################################################################
=head2 image_rotate
This leaves the definition orthoganal, expands the underlying image
object, and resets the insert point and size properties accordingly.
$drw->image_rotate($addr, $angle, \@point);
The current implementation does not handle the change to the image
clipping boundary.
=cut
sub image_rotate {
my $dbg = 0;
my $check = 0;
# FIXME: must be a better way to do this:
lib/CAD/Drawing/Manipulate/Graphics.pm view on Meta::CPAN
my @insert = @{$ext[0]};
$obj->{pt} = [@insert];
$dbg && print "new insert: @insert\n";
$check && $scrpad->show(hang=>1);
$check && exit;
# set the size and we're done
$obj->{size} = [$w, $h];
} # end subroutine image_rotate definition
########################################################################
=head2 image_swap_context
This involves a scaling of the image (the contexts should be aligned
over each other at this point or everything will go to hell.) Do your
own move / rotate / crop before calling this, because all this does is
to scale the underlying image object such that the vec property of the
image definition at $dest_addr can be used correctly.
Note that this does not "swap" the image to $dest_addr, rather it uses
the image definition of $dest_addr to change the image object and
definition at $source_addr.
Also note that the image must fit completely inside (I think) of the
destination in order for the composite to work correctly.
$drw->image_swap_context($source_addr, $dest_addr);
=cut
sub image_swap_context {
my $dbg = 0;
my $self = shift;
my ($s_addr, $d_addr) = @_;
my $bgcolor = "gold";
($s_addr->{type} eq "images") or croak("not an image\n");
($d_addr->{type} eq "images") or croak("not an image\n");
my $obj = $self->getobj($s_addr);
# note: we will kill this one:
my $im_in = $obj->{image_handle};
# determine the scale difference between the two definitions
my $dvecs = $self->Get("vector", $d_addr);
my $svecs = $self->Get("vector", $s_addr);
my @scale = (
$dvecs->[0][0] / $svecs->[0][0],
$dvecs->[1][1] / $svecs->[1][1],
);
$dbg && print "vecs scale at @scale\n";
my ($w, $h) = map({sprintf("%0.0f", $_ * $scale[0])}
$im_in->Get("width", "height")
);
$im_in->Scale("width" => $w, "height" => $h);
$dbg && print "size now $w x $h (hopefully)\n";
$dbg && print "checking: ",
join(" x ", $im_in->Get("width", "height")), "\n";
# and set the vecs
$obj->{vector} = [map({[@$_]} @$dvecs)];
# and the size
$obj->{size} = [$w, $h];
# need to create a new image object which represents the destination
# size and find the points where this one fits into that.
my $d_size = $self->Get("size", $d_addr);
my $im_out = Image::Magick->new();
$im_out->Set(size => sprintf("%0.0fx%0.0f", @$d_size));
$dbg && print "filling new image at @$d_size\n";
$im_out->Read("xc:$bgcolor");
$im_out->Transparent("color" => $bgcolor);
# dot each corner for justification into other images
my $color = $aci2hex[$self->Get("color", $s_addr)];
$dbg && print "output dot color: $color\n";
my $x = $d_size->[0] - 1;
my $y = $d_size->[1] - 1;
$im_out->Set("pixel[0,0]" => $color);
$im_out->Set("pixel[$x,0]" => $color);
$im_out->Set("pixel[0,$y]" => $color);
$im_out->Set("pixel[$x,$y]" => $color);
# determine placement from 0,0 of source mapped onto dest:
my @placement = map({sprintf("%0.0f", $_)}
$self->drw_to_img([$self->img_to_drw([0,0], $s_addr)], $d_addr)
);
$dbg && print "compositing...\n";
$im_out->Composite(
compose => "Over", image => $im_in,
x => $placement[0], y => $placement[1]
);
$dbg && print "done\n";
$obj->{image_handle} = $im_out;
undef($im_in);
# set the size, so it will be proper
} # end subroutine image_swap_context definition
########################################################################
1;
( run in 0.773 second using v1.01-cache-2.11-cpan-39bf76dae61 )