summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ChangeLog4
-rw-r--r--README4
-rw-r--r--data/style.css7
-rw-r--r--lib/Multi/RG.pm91
-rw-r--r--lib/VNDB/DB/VN.pm4
-rw-r--r--lib/VNDB/Handler/VNPage.pm23
-rwxr-xr-xutil/init.pl2
-rw-r--r--util/updates/update_2.8.sql16
9 files changed, 113 insertions, 39 deletions
diff --git a/.gitignore b/.gitignore
index 59f221ff..2baf1a87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
/static/s/*/style.css
/static/s/*/boxbg.png
/static/cv/
-/static/rg/
/static/sf/
/static/st/
/static/robots.txt
diff --git a/ChangeLog b/ChangeLog
index 2a269f21..9cb2bbd3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+git - ?
+ - Converted relation graphs to use inline SVG
+ - Relation graphs now use the color scheme of selected skin
+
2.7 - 2009-09-24
- Improved styling of the threeboxes layout
- Blacklist a users' votes from the VN vote statistics
diff --git a/README b/README
index 5177b6e2..1060c544 100644
--- a/README
+++ b/README
@@ -12,7 +12,7 @@ Requirements
global requirements:
Linux, or an OS that resembles Linux. Chances are VNDB won't run on Windows.
- PostgreSQL 8.3 recommended, 8.1 may also work
+ PostgreSQL 8.3+
perl 5.10 recommended, 5.8 may also work
A webserver that works with YAWF (lighttpd and Apache are known to work)
@@ -41,6 +41,8 @@ Requirements
Maintenance:
PerlIO::gzip
RG:
+ XML::Parser
+ XML::Writer
graphviz (/usr/bin/dot is used by default)
Sitemap:
XML::Writer
diff --git a/data/style.css b/data/style.css
index 53255c74..0772dda8 100644
--- a/data/style.css
+++ b/data/style.css
@@ -1182,3 +1182,10 @@ a .icons { cursor: pointer }
.icons.ko { background-position: -61px -88px; }
.icons.vi { background-position: -61px -99px; }
+
+/* Relation graph colors */
+svg .border { fill: none; stroke: $border$ }
+svg .edge polygon.border { fill: $border$ }
+svg .nodebg { fill: $tabbg$; stroke: $tabbg$ }
+svg text { fill: $maintext$ }
+
diff --git a/lib/Multi/RG.pm b/lib/Multi/RG.pm
index e427caf2..4ae0516e 100644
--- a/lib/Multi/RG.pm
+++ b/lib/Multi/RG.pm
@@ -9,6 +9,8 @@ use strict;
use warnings;
use POE 'Wheel::Run', 'Filter::Stream';
use Encode 'encode_utf8';
+use XML::Parser;
+use XML::Writer;
use Time::HiRes 'time';
@@ -17,14 +19,13 @@ sub spawn {
POE::Session->create(
package_states => [
$p => [qw|
- _start shutdown check_rg creategraph getrel builddot buildgraph savegraph
+ _start shutdown check_rg creategraph getrel builddot savegraph finish
proc_stdin proc_stdout proc_stderr proc_closed proc_child
|],
],
heap => {
font => 'Arial',
fsize => [ 9, 7, 10 ], # nodes, edges, node_title
- imgdir => '/www/vndb/static/rg',
moy => [qw| Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec |],
dot => '/usr/bin/dot',
check_delay => 3600,
@@ -104,12 +105,10 @@ sub builddot { # num, res
my $gv =
qq|graph rgraph {\n|.
- qq|\tratio = "compress"\n|.
- qq|\tgraph [ bgcolor="#ffffff00" ]\n|.
qq|\tnode [ fontname = "$_[HEAP]{font}", shape = "plaintext",|.
- qq| fontsize = $_[HEAP]{fsize}[0], style = "setlinewidth(0.5)", fontcolor = "#cccccc", color = "#225588" ]\n|.
+ qq| fontsize = $_[HEAP]{fsize}[0], fontcolor = "#333333", color = "#111111" ]\n|.
qq|\tedge [ labeldistance = 2.5, labelangle = -20, labeljust = 1, minlen = 2, dir = "both",|.
- qq| fontname = $_[HEAP]{font}, fontsize = $_[HEAP]{fsize}[1], arrowsize = 0.7, color = "#225588", fontcolor = "#cccccc" ]\n|;
+ qq| fontname = $_[HEAP]{font}, fontsize = $_[HEAP]{fsize}[1], arrowsize = 0.7, color = "#111111", fontcolor = "#333333" ]\n|;
# insert all nodes, ordered by release date
for (sort { $a->{date} <=> $b->{date} } @$vns) {
@@ -128,7 +127,7 @@ sub builddot { # num, res
$gv .= sprintf
qq|\tv%d [ URL = "/v%d", tooltip = "%s" label=<|.
- q|<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="1" BGCOLOR="#00000033">|.
+ q|<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="1" BGCOLOR="#222222">|.
q|<TR><TD COLSPAN="2" ALIGN="CENTER" CELLPADDING="2"><FONT POINT-SIZE="%d"> %s </FONT></TD></TR>|.
q|<TR><TD> %s </TD><TD> %s </TD></TR>|.
qq|</TABLE>> ]\n|,
@@ -161,49 +160,79 @@ sub builddot { # num, res
$gv .= "}\n";
- # get ID
- $_[KERNEL]->post(pg => query => 'INSERT INTO relgraph (cmap) VALUES (\'\') RETURNING id', undef, 'buildgraph', \$gv);
-}
-
-
-sub buildgraph { # num, res, \$gv
- $_[HEAP]{gid} = $_[ARG1][0]{id};
- $_[HEAP]{graph} = sprintf('%s/%02d/%d.png', $_[HEAP]{imgdir}, $_[ARG1][0]{id} % 100, $_[ARG1][0]{id});
- $_[HEAP]{cmap} = '';
-
- # roughly equivalent to:
- # cat layout.txt | dot -Tpng -o graph.png -Tcmapx
+ # Pass our dot file to graphviz
+ $_[HEAP]{svg} = '';
$_[HEAP]{proc} = POE::Wheel::Run->new(
Program => $_[HEAP]{dot},
- ProgramArgs => [ '-Tpng', '-o', $_[HEAP]{graph}, '-Tcmapx' ],
+ ProgramArgs => [ '-Tsvg' ],
StdioFilter => POE::Filter::Stream->new(),
StdinEvent => 'proc_stdin',
StdoutEvent => 'proc_stdout',
StderrEvent => 'proc_stderr',
CloseEvent => 'proc_closed',
);
- $_[HEAP]{proc}->put(${$_[ARG2]});
+ $_[HEAP]{proc}->put($gv);
}
sub savegraph {
- my $vids = join ',', sort map int, keys %{$_[HEAP]{nodes}};
+ # Before saving the SVG output, we'll modify it a little:
+ # - Remove comments
+ # - Add svg: prefix to all tags
+ # - Remove xmlns declarations (this is set in the html)
+ # - Remove <title> elements (unused)
+ # - Remove id attributes (unused)
+ # - Remove first <polygon> element (emulates the background color)
+ # - Replace stroke and fill attributes with classes (so that coloring is done in CSS)
+ my $svg = '';
+ my $w = XML::Writer->new(OUTPUT => \$svg);
+ my $p = XML::Parser->new;
+ $p->setHandlers(
+ Start => sub {
+ my($expat, $el, %attr) = @_;
+ return if $el eq 'title' || $expat->in_element('title');
+ return if $el eq 'polygon' && $expat->depth == 2;
+
+ $attr{class} = 'border' if $attr{stroke} && $attr{stroke} eq '#111111';
+ $attr{class} = 'nodebg' if $attr{fill} && $attr{fill} eq '#222222';
+
+ delete @attr{qw|stroke fill id xmlns xmlns:xlink|};
+ $el eq 'path' || $el eq 'polygon'
+ ? $w->emptyTag("svg:$el", %attr)
+ : $w->startTag("svg:$el", %attr);
+ },
+ End => sub {
+ my($expat, $el) = @_;
+ return if $el eq 'title' || $expat->in_element('title');
+ return if $el eq 'polygon' && $expat->depth == 2;
+ $w->endTag("svg:$el") if $el ne 'path' && $el ne 'polygon';
+ },
+ Char => sub {
+ my($expat, $str) = @_;
+ return if $expat->in_element('title');
+ $w->characters($str) if $str !~ /^[\s\t\r\n]*$/s;
+ }
+ );
+ $p->parsestring($_[HEAP]{svg});
+ $w->end();
- # chmod graph
- chmod 0666, $_[HEAP]{graph};
+ # save the processed SVG in the database and fetch graph ID
+ $_[KERNEL]->post(pg => query => 'INSERT INTO vn_graphs (svg) VALUES (?) RETURNING id', [ $svg ], 'finish');
+}
- # save the image map in the database
- $_[KERNEL]->post(pg => do => 'UPDATE relgraph SET cmap = ? WHERE id = ?',
- [ "<!-- V:$vids -->\n$_[HEAP]{cmap}", $_[HEAP]{gid} ]);
+
+sub finish { # num, res
+ my $id = $_[ARG1][0]{id};
+ my $vids = join ',', sort map int, keys %{$_[HEAP]{nodes}};
# update the VN table
- $_[KERNEL]->post(pg => do => "UPDATE vn SET rgraph = ? WHERE id IN($vids)", [ $_[HEAP]{gid} ]);
+ $_[KERNEL]->post(pg => do => "UPDATE vn SET rgraph = ? WHERE id IN($vids)", [ $id ]);
# log
- $_[KERNEL]->call(core => log => 'Generated relation graph in %.2fs, V: %s', time-$_[HEAP]{start}, $vids);
+ $_[KERNEL]->call(core => log => 'Generated VN relation graph #%d in %.2fs, V: %s', $id, time-$_[HEAP]{start}, $vids);
# clean up
- delete @{$_[HEAP]}{qw| start vid nodes rels gid graph cmap proc |};
+ delete @{$_[HEAP]}{qw| start vid nodes rels svg proc |};
# check for more things to do
$_[KERNEL]->yield('check_rg');
@@ -216,7 +245,7 @@ sub proc_stdin {
$_[HEAP]{proc}->shutdown_stdin;
}
sub proc_stdout {
- $_[HEAP]{cmap} .= $_[ARG0];
+ $_[HEAP]{svg} .= $_[ARG0];
}
sub proc_stderr {
$_[KERNEL]->call(core => log => 'GraphViz STDERR: %s', $_[ARG0]);
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index b3603985..c6499350 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -76,7 +76,7 @@ sub dbVNGet {
$o{what} =~ /changes/ ?
'JOIN users u ON u.id = c.requester' : (),
$o{what} =~ /relgraph/ ?
- 'JOIN relgraph rg ON rg.id = v.rgraph' : (),
+ 'JOIN vn_graphs vg ON vg.id = v.rgraph' : (),
);
my $tag_ids = $o{tags_include} && join ',', @{$o{tags_include}[1]};
@@ -86,7 +86,7 @@ sub dbVNGet {
qw|vr.alias vr.image vr.img_nsfw vr.length vr.desc vr.l_wp vr.l_encubed vr.l_renai vr.l_vnn| ) : (),
$o{what} =~ /changes/ ? (
qw|c.requester c.comments v.latest u.username c.rev c.causedby|, q|extract('epoch' from c.added) as added|) : (),
- $o{what} =~ /relgraph/ ? 'rg.cmap' : (),
+ $o{what} =~ /relgraph/ ? 'vg.svg' : (),
$o{what} =~ /ranking/ ? '(SELECT COUNT(*)+1 FROM vn iv WHERE iv.hidden = false AND iv.c_popularity > v.c_popularity) AS ranking' : (),
$tag_ids ?
qq|(SELECT AVG(tvb.rating) FROM tags_vn_bayesian tvb WHERE tvb.tag IN($tag_ids) AND tvb.vid = v.id AND spoiler <= $o{tags_include}[0] GROUP BY tvb.vid) AS tagscore| : (),
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index 361963a8..5e0fc9dc 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -23,20 +23,37 @@ sub rand {
sub rg {
my($self, $vid) = @_;
+ # TODO: browser detection + notice, this trick gives some ugly results in IE
+
my $v = $self->dbVNGet(id => $vid, what => 'relgraph')->[0];
return 404 if !$v->{id} || !$v->{rgraph};
my $title = mt '_vnrg_title', $v->{title};
+ $self->resHeader('Content-Type' => 'application/xhtml+xml');
+
+ # This is a REALLY ugly hack, need find a proper solution in YAWF
+ no warnings 'redefine';
+ my $sub = \&YAWF::XML::html;
+ *YAWF::XML::html = sub () {
+ lit q|<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
+ "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">|;
+ tag 'html',
+ xmlns => "http://www.w3.org/1999/xhtml",
+ 'xmlns:svg' => 'http://www.w3.org/2000/svg',
+ 'xmlns:xlink' => 'http://www.w3.org/1999/xlink';
+ };
$self->htmlHeader(title => $title);
+ *YAWF::XML::html = $sub;
+
$self->htmlMainTabs('v', $v, 'rg');
div class => 'mainbox';
h1 $title;
- lit $v->{cmap};
p class => 'center';
- img src => sprintf('%s/rg/%02d/%d.png', $self->{url_static}, $v->{rgraph}%100, $v->{rgraph}),
- alt => $title, usemap => '#rgraph';
+ lit $v->{svg};
end;
end;
+ $self->htmlFooter;
}
diff --git a/util/init.pl b/util/init.pl
index 5c171705..ac533174 100755
--- a/util/init.pl
+++ b/util/init.pl
@@ -22,7 +22,7 @@ print "\n";
print "Creating directory structures...\n";
-for my $d (qw| cv rg st sf |) {
+for my $d (qw| cv st sf |) {
print " /static/$d\n";
mkdir "$ROOT/static/$d" or die "mkdir '$ROOT/static/$d': $!\n";
for my $i (0..99) {
diff --git a/util/updates/update_2.8.sql b/util/updates/update_2.8.sql
new file mode 100644
index 00000000..a48c1e73
--- /dev/null
+++ b/util/updates/update_2.8.sql
@@ -0,0 +1,16 @@
+
+-- !BEFORE! running this SQL file, make sure to kill Multi,
+-- After running this SQL file, also make sure to do a:
+-- $ rm -r static/rg/
+-- And start multi again
+
+-- VN Relation graphs are stored in the database as SVG - no cmaps and .png anymore
+UPDATE vn SET rgraph = NULL;
+ALTER TABLE vn DROP CONSTRAINT vn_rgraph_fkey;
+DROP TABLE relgraph;
+CREATE TABLE vn_graphs (
+ id SERIAL PRIMARY KEY,
+ svg xml NOT NULL
+);
+ALTER TABLE vn ADD FOREIGN KEY (rgraph) REFERENCES vn_graphs (id);
+