Chart-EPS_graph

 view release on metacpan or  search on metacpan

lib/Chart/EPS_graph.pm  view on Meta::CPAN

	$self->{data}        = [];
	$self->{y1}          = [];
	$self->{y2}          = [];
	$self->{shown}       = [];
	$self->{not_shown}   = [];
	$self->{close_gap}   = 0;
	$self->{x_is_zeroth} = 1;
	$self->{x_scale}     = 1;
	$self->{ps_path}     = $EMPTY;
	$self->{verbosity}   = 1; # 0 = Quiet, 1 = Info, 2 = Diagnostic.
	bless $self, $class;
	return $self;
}

# Allow user to change defaults and input data.
sub set {
	ref( my $self = shift ) or croak 'Oops! Method set() is instance, not class.';
	my %user_defs = @_;
	while ( my ($key, $value) = each %user_defs ) {
		if ( exists $self->{$key}) {$self->{$key} = $value }
		else { carp "Oops! Key '$key' non-existant in hash '$self'.\n" }
	}                                             
	return 'Pthhht! to Perl::Critic';
}

# Make custom adjustments to default Prolog defs.
sub ps_defs_insert {
	ref( my $self = shift ) or croak 'Oops! Method ps_defs_insert() is instance, not class.';
	$self->chans_elect();
	my $str = ();

	$str .= "/bg_color ($self->{bg_color}) def \n";
	$str .= "/fg_color ($self->{fg_color}) def \n";
	$str .= '/web_colors [ ';
	for ( @{$self->{web_colors}} ) { $str .= "/$_ " }
	$str .= "] def \n";

	$str .= "/font_name /$self->{font_name} def \n";
	$str .= "/font_size $self->{font_size} def \n";


	# Set not-to-be-shown labels as empty strings.
	$str .= "/label_top ($self->{label_top}) def \n";
	$str .= "/label_y1 ($self->{label_y1}) def \n";
	$str .= "/label_y1_2 ($self->{label_y1_2}) def \n";
	$str .= "/label_y2 ($self->{label_y2}) def \n";
	$str .= "/label_y2_2 ($self->{label_y2_2}) def \n";
	$str .= "/label_x ($self->{label_x}) def \n";
	$str .= "/label_x_2 ($self->{label_x_2}) def \n";

	$str .= '/fake_col_zero_flag ';
	$str .= $self->{x_is_zeroth} ? 'false' : 'true';
	$str .= " def \n";

	$str .= "/fake_col_zero_scale $self->{x_scale} def \n";

	$str .= "/data_sets 1 def \n";

	# This is an ugly, ex-post-facto hack.
	# When chans skipped, patch up the bottom string to cover up remove gap in
	# channel ID's. In short, make legend ID match the gap-free curve ID.
	if ($self->{close_gap}) {
		$self->{shown} = [ @{ $self->{y1} }, @{ $self->{y2} } ];
		my @gap_free = gap_free_skip( $self->{shown}, $self->{not_shown} );
		for my $i ( 0 .. $#gap_free ) {
			$self->{label_x_proc} =~ s/  $self->{shown}->[$i] /  $gap_free[$i] /m;
			$self->{label_x_proc} =~ s/  $self->{shown}->[$i] show_color_id/ $gap_free[$i] show_color_id/m;
		}
	}

	$str .= $self->{label_x_proc}; # List of chans shown

	# An array of data chans embeded in PostScript but whose curves are not to be shown
	# and their colors skipped over by those curves which are shown.
	$str .= '/not_shown [ ';
	$str .= join q{ }, @{$self->{not_shown}} unless $self->{close_gap}; # 'Pthhht!'
	$str .= " ] def \n";

	# Y2 axis (re-)numbered for PostScript.
	$str .= '/y2 [';
	if ($self->{close_gap}) {
		$str .= join q{ }, gap_free_skip( $self->{y2}, $self->{not_shown} );
	}
	else {
		$str .= join q{ }, @{ $self->{y2} };
	}

	$str .= "] def \n";    # Y2 axis
	return $str;
}

# Given two arrays retrun a copy of 2nd array after decrementing its elements
# for each lesser element of 1st array. Used to provide PostScript with /column_arrays
# named 0 thru N with no gaps when chans have been skipped over to graph.
sub gap_free_skip {
	my ( $shown_aref, $not_shown_aref ) = @_; # Local, not part of $self->{foo} hash!
	my @gap_free = @$shown_aref;
	for my $i ( 0 .. $#gap_free ) {
		for my $j (@$not_shown_aref) {

			# Index decremented for each gap beneath it.
			if ( $j < $shown_aref->[$i] ) { --$gap_free[$i] }
		}
	}
	return @gap_free;
}

# Assign an arrow character from Symbol font to
# indicate which Y axis ought be read from.
sub y_arrow {
	ref( my $self = shift ) or croak 'Oops! Method y_arrow() is instance, not class.';
	my $i = shift;
	my $arrow = $EMPTY;
	my @y2 = @{$self->{y2}};
	if (@y2) {
		$arrow = '\254';
		for (@y2) { $arrow = '\256' if $_ == $i}
	}
	return $arrow;
}

# PostScript strings are ( ) delimited!
sub ps_str_esc {
	ref( my $self = shift ) or croak 'Oops! Method ps_str_esc() is instance, not class.';
	$_[0] =~ s/\(/\\(/gm;
	$_[0] =~ s/\)/\\)/gm;
	$self->verbose( "ps_str_esc(): $_[0] \n", 2);
	return $_[0];
}

# Build label for chans shown, list of those not to show.
sub chans_elect {
	ref( my $self = shift ) or croak 'Oops! Method chans_elect() is instance, not class.';

	for ( @{ $self->{names} } ) { $_ = $self->ps_str_esc($_) }

	my @ps_string_list = qw(
		label_top
		label_x
		label_x_2
		label_y1
		label_y1_2
		label_y2
		label_y2_2
	);

	for (@ps_string_list) { $self->{$_} = $self->ps_str_esc($self->{$_}) }

	# Collect list of shown-channel names
	# Prettify them into a graph legend good for B&W, not just color.
	for (@{ $self->{y1} }, @{ $self->{y2} }) {
		my $arrow = $self->y_arrow($_);
		$self->{label_x_proc} .= " ($arrow$_) $_ show_color_id ($self->{names}->[$_]  ) show ";
	}

	# Determine list of channels not to show.
	for ( 1 .. $#{$self->{names}} ) {

		# Mitigate an RE between Perl & PostScript by swaping all
		# escaped-for-PostScript string delimiters with Perl RE dots.
		my $re = $self->{names}->[$_];
		$re =~ tr/\\()/.../;

		push @{$self->{not_shown}}, $_ unless $self->{label_x_proc} =~ m/$re/m;

		# Problems may still exist between Perl RE's and PostScript syntax.
		# Diagnose these via CLI if in doubt by setting $verbosity = 2.
		$self->verbose(
			"RE Check 1: \n\t" . $self->{label_x_proc}
			. "\n\t =~ \n\t$re" . "\n"
			, 2
		);
		$self->verbose(
			'RE Check 2: not_shown = '
			. join(', ', @{$self->{not_shown}}) . "\n\n"
			, 2
		);
	}

	$self->{label_x_proc} = "/label_x_proc { $self->{label_x_proc} } def \n";
	$self->verbose( "LABEL X PROC: $self->{label_x_proc} \n", 2);
	return 'Pthhht! to Perl::Critic';
}

# Output data in PostScript file format.
sub write_eps {
	ref( my $self = shift ) or croak 'Oops! Method write_eps() is instance, not class.';
	$self->{ps_path} = qq|$_[0]|;
	local $OUTPUT_AUTOFLUSH = 1; # from 'use ENGLISH'

	my ( $ps_user_defs ) = $self->ps_defs_insert();

	if ( open my $fh, '>', "$self->{ps_path}" ) {

		# Embed filename sans path in PostScript header.
		$self->{ps_header} =~ s/%%Title:/%%Title: ($self->{ps_path})/m;

		# Embed document font resources.
		my $doc_rsrcs = "font Symbol $self->{font_name}";
		$self->{ps_header} =~ s/%%DocumentResources:/%%DocumentResources: $doc_rsrcs/m;

		# IMPORTANT NOTE: Know that Perl::Critic errs about the package vars
		# named like "$Chart::EPS_graph::PS::ps_foo" below. They are needed!

		print {$fh} $self->{ps_header};
		print {$fh} $Chart::EPS_graph::PS::ps_web_colors_dict;
		print {$fh} $Chart::EPS_graph::PS::ps_prolog_generic;
		print {$fh} $Chart::EPS_graph::PS::ps_prolog_graphing;
		print {$fh} $Chart::EPS_graph::PS::ps_prolog_data_arrays;
		print {$fh} $Chart::EPS_graph::PS::ps_prolog_drawing;



( run in 3.665 seconds using v1.01-cache-2.11-cpan-df04353d9ac )