Как я могу программно сгенерировать изображения диаграммы Венна с метками в верхней части изображения? - PullRequest
3 голосов
/ 20 сентября 2011

Я пытаюсь сгенерировать диаграммы Венна для отчета в формате PDF с текстом поверх отдельных областей.

Мы используем htmldoc для создания PDF-файлов, что исключает текст поверх фоновых изображений.

Мы используем API Google Charts для других изображений, но их диаграммы Венна не поддерживают текст в верхней части диаграммы (из того, что я могу сказать).

Самым простым способом было бы создать образ венны на нашем сервере с помощью сторонней библиотеки, а затем связать изображение с документом. Я просто не знаю никаких пакетов программного обеспечения, которые бы поддерживали наш вариант использования. .

Любые ссылки / указатели приветствуются.

Ответы [ 4 ]

3 голосов
/ 20 сентября 2011

Вот пример кода. Это похоже на достойный учебник:

http://paulbourke.net/dataformats/postscript/

Если вы работаете в Linux, вы можете использовать команду gv для просмотра. Существуют различные утилиты для преобразования его в PDF; ps2pdf в Linux, и я думаю, что Acrobat Distiller в Windows.

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 144 144

% CenterText - paint text centered on x with baseline on y
% x y s CenterText
/CenterText
{
   << >> begin
   /s exch def /y exch def /x exch def
   newpath x s stringwidth pop 2 div sub y moveto s show
   end
} bind def

2 setlinewidth
54 72 36 0 360 arc stroke
90 72 36 0 360 arc stroke

/Helvetica 10 selectfont
36 72 (A) CenterText
108 72 (B) CenterText
72 72 (A^B) CenterText

Вот три круга. Это работает, но я не ручаюсь за качество кодирования, я не делал серьезных PS-кодов годами.

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 216 216

% CenterText - paint text centered on x with baseline on y
% x y s CenterText
/CenterText
{
   << >> begin
   /s exch def /y exch def /x exch def
   newpath x s stringwidth pop 2 div sub y moveto s show
   end
} bind def

% Set center of bounding box at 0,0 and rotate 90 degrees cw
108 108 translate
gsave
180 rotate

% Draw 3 circles at 120-degree intervals
/ct 3 def
/offset 36 def
/radius 60 def
0 1 ct 1 sub   % for
{
    gsave
    360 mul ct div rotate
    0 offset translate
    0 0 radius 0 360 arc stroke
    grestore
} for

grestore

/Helvetica 10 selectfont
-54 36 (A) CenterText
54 36 (B) CenterText
0 -72 (C) CenterText

0 36 (A^B) CenterText
-36 -24 (A^C) CenterText
36 -24 (B^C) CenterText

0 -6 (A^B^C) CenterText
1 голос
/ 11 октября 2011

Почему бы просто не использовать LaTeX?

Гораздо проще, чем писать вручную ps:

\tikz \fill[even odd rule] (0,0) circle (1) (1,0) circle (1);

1 голос
/ 23 сентября 2011

Пойдя настолько далеко, насколько это практически возможно с pic, postscript действительно является естественным выбором для этого.

Хорошо, я еще не решил маркировку, но вот обобщенная диаграмма. Оказывается, вы просто помещаете центры в вершины правильного многоугольника для этого n .

Но некоторые из этих мест становятся реальными маленькими. Так что я думаю о некотором шаблоне маркированных дуг, растущих по спирали. Возможно, радиус метки должен отражать глубину обозначенной перегородки ...

Редактировать: Я переработал код, поэтому в ревизии 1 есть симпатичная страница с 15 диаграммами.

Редактировать: Я только что получил образование в Википедии. Оказывается, то, что я называю диаграммой Венна с четырьмя ячейками, является , фактически, вообще не диаграммой Венна .

4-pentagon Euler diagram

Это диаграмма Эйлера. Проблема в том, что нигде нельзя получить пересечение двух областей в одиночку с противоположных сторон диаграммы. Диаграмма real 4 ячейки становится странной, независимо от того, как вы это делаете. Таким образом, объем ответа уменьшен по сравнению с тем, что я преследовал в последних двух изменениях.

Для диаграммы с двумя кругами лучшее расположение, которое я могу найти, определяется пересечением радиусов от центра диаграммы через центры окружностей к краям, с определяющими кругами, помещенными в центры окружностей. 2-circle Euler diagram with radii and duplicate defining circles

Для 3-круговой диаграммы лучшее расположение, которое я могу найти, определяется пересечениями радиусов (и повернутых радиусов) с приближением повернутых треугольников к окружностям и неповоротным треугольникам соответственно. 3-circle Euler diagram with apparatus

Вариант кода можно найти в предыдущей редакции этого ответа. Выложил расширенную версию для usenet в теме геодезические цветы . Но поскольку этот ответ излишним (и на самом деле он не рисует никаких ярлыков и не возвращает их местоположения) и недостаточным для обобщенных диаграмм Венна real , мне придется подрезать большую часть багажа, прежде чем подвергать это вопрос к более длинным блокам кода.

Редактировать: Я думаю, что это почти облизано. Эта программа содержит только те части предыдущей программы, которые необходимы для создания 2- и 3-диаграмм Венна с маленькими кружочками в «идеальных» местах меток. Для диаграммы с двумя ячейками решение действительно тривиально (удваивает определяющий радиус). Для диаграммы с 3 ячейками решение составляет cos (60) * радиус окружности + определяющий радиус , либо умножение сначала или добавление сначала.

Редактировать: Наконец, ярлыки. Требовалась некоторая хитрость в последнюю минуту, так как я использовал вращение матрицы, чтобы найти точки. Это означало, что когда я пытался печатать этикетки, все они были в странной ориентации. Так что процедура "centershow" имеет немного больше, чем обычно. Он должен сбрасывать части масштабирования текущей матрицы преобразования, оставляя компоненты перевода в покое. Это означает, что где-то на раннем этапе выполнения мы должны хранить ориентированную матрицу в правильном масштабе.

( Редактировать: Еще один способ получить текст в вертикальном положении без изменения матрицы - это transform расположение координат устройства, установка ориентированной матрицы (в любом масштабе или переводе!), itransform точка возврата к «новым» пользовательским координатам, а затем moveto.)

%!

%cp:xy  rad  circ  -
/circ {
    currentpoint newpath
        2 copy 5 -1 roll 0 360 arc stroke
    moveto
} def

%rad n  poly  [pointlist]
/poly {
    1 dict begin exch /prad exch def
    [ exch
        0 exch 360 exch div 359.9 {
            [ exch
                dup cos prad mul exch
                sin prad mul
            ]
        } for
    ]
    end
} def

%[list] rad  subcirc  -
/subcirc {
    1 dict begin /crad exch def gsave
        currentpoint translate
        { aload pop moveto crad circ } forall
    grestore end
} def

%[list]  locate  -
%draw little circles around each point
/locate {
    gsave
        currentpoint translate
        0 0 moveto 5 circ
        { aload pop moveto 5 circ } forall
    grestore
} def

%cp:xy  (string)  cshow  -
/cshow {
    gsave
        currentpoint translate %0 0 moveto
        matrix currentmatrix
        dup 0 normal 0 4 getinterval %reset rotation, keep translation
        putinterval setmatrix
        dup true charpath flattenpath pathbbox
        3 -1 roll sub 3 1 roll sub
        2 div exch -2 div moveto show
    grestore
} def

%[list] [labels]  label  -
%print label text centered on each point
/label {
    gsave
        currentpoint translate
        0 1 3 index length 1 sub {
            2 index 1 index get aload pop moveto
            2 copy get cshow pop
        } for
        pop pop
    grestore
} def

%[x0 y0] [x1 y1]  pyth-dist  radius
/pyth-dist {
    aload pop 3 -1 roll aload pop % x1 y1 x0 y0
    exch % x1 y1 y0 x0
    3 1 roll sub dup mul % x1 x0 dy^2
    3 1 roll sub dup mul % dy^2 dx^2
    add sqrt
} def

/rotw { 180 n div rotate } def

%cp:xy rad n  venn  -
%make the circles intersect the opposite point of def poly
/venn {
    3 dict begin /n exch def /vrad exch def
        vrad n poly
        dup 0 get exch
        dup length 2 idiv get
        pyth-dist /crad exch def
        %vrad crad n ven
        vrad n poly crad subcirc  %the Venn circles
        [[0 0]] [(All)] label
        n 2 eq {
            %vrad 2 mul n poly locate
            vrad 2 mul n poly
            [(A) (B)] label
        }{
            n 3 eq {
                %vrad crad 60 cos mul add n poly locate
                vrad crad 60 cos mul add n poly
                [ (A) (B) (C) ] label
                %gsave rotw vrad crad add 60 cos mul n poly locate grestore
                gsave rotw vrad crad add 60 cos mul n poly
                [ (A^B) (B^C) (A^C) ] label
                grestore
            } if
        } ifelse
    end
} def

/normal matrix currentmatrix def
/in{72 mul}def
/Palatino-Roman 20 selectfont
4.25 in 8.25 in moveto
1 in 2 venn
4.25 in 3.5 in moveto
1 in 3 venn
showpage

И ghostscript выдает (gs -sDEVICE=jpeggray -sOutputFile=venlabel.jpg v4.ps): 2- and 3-Venn diagrams with labels

1 голос
/ 22 сентября 2011

Вот схема с двумя ячейками на рис. Я нашел, что эллипсы легче втиснуть в текст, чем в кружки.

.PS
ellipse
"A" at 1st ellipse - (.2, 0)
ellipse with .w at 1st ellipse.e - (.4, 0)
"B" at 2nd ellipse + (.2, 0)
"A^B" at 1st ellipse.e - (.2, 0)
.PE

И трехэлементная схема:

.PS
ellipsewid = 1
ellipseht = .75
ellipse
ellipse at 1st ellipse + (.5, 0)
ellipse at 1st ellipse + (.25, .35)
"A" at 1st ellipse - (.2, .1)
"B" at 2nd ellipse + (.2, -.1)
"C" at 3rd ellipse + (0, .1)
"A^B" at 3rd ellipse - (0, .5)
"A^C" at 3rd ellipse - (.3, .1)
"B^C" at 3rd ellipse + (.3, -.1)
"A^B^C" at 3rd ellipse - (0, .25)
.PE

Конвертировать в пс: groff -p ven.pic > ven.ps.

Я пока не нашел изящного способа производства .eps. Оставайтесь в курсе! Редактировать: sudo apt-get install ps2eps!

Edit:

Гораздо проще построить все относительно точек компаса на центральном невидимом блоке.

двухкамерный:

.PS
box invis "A^B"
ellipse wid 1st box.wid*1.5 at 1st box.w + (.1, 0)
ellipse wid 1st box.wid*1.5 at 1st box.e - (.1, 0)
"A  " at 2nd ellipse.w rjust
"  B" at 1st ellipse.e ljust
.PE

Три ячейки:

.PS
box invis "A^B^C" below wid .5 ht .3
ellipse at 1st box.sw
ellipse at 1st box.se
ellipse at 1st box.n
"A  " at 2st ellipse.w rjust below
"  B" at 1nd ellipse.e ljust below
"C" "" "" at 3rd ellipse above
"A^B" at 3rd ellipse.s below
"A^C   " at 2nd ellipse.nw rjust
"   B^C" at 1nd ellipse.ne ljust
.PE

Тем не менее, требуется настройка. Но цифр гораздо меньше! Ширина и высота прямоугольника определяют равнобедренный треугольник, используемый для размещения центров эллипсов.

Edit:

Эта последняя идея предлагает метод создания четырехэлементной диаграммы. Мне пришлось уменьшить шрифт для клиньев.

.PS
box invis "A^B^C^D" wid .65 ht .5
ellipsewid = 2
ellipseht = 1.25
ellipse at 1st box.ne
ellipse at 1st box.se
ellipse at 1st box.sw
ellipse at 1st box.nw
"A" at 1st box.ne + (.4,  .4)
"B" at 1st box.se + (.4, -.4)
"C" at 1st box.sw - (.4,  .4)
"D" at 1st box.nw - (.4, -.4)
"A^B" at 1st box.e + (.4, 0) ljust
"B^C" at 1st box.s - (0, .2) below
"C^D" at 1st box.w - (.4, 0) rjust
"A^D" at 1st box.n + (0, .2) above
"\s-1A^B^D\s+1" at 1st box.ne + (.15,  .03)
"\s-1A^B^C\s+1" at 1st box.se + (.15, -.03)
"\s-1B^C^D\s+1" at 1st box.sw - (.15,  .03)
"\s-1A^C^D\s+1" at 1st box.nw - (.15, -.03)
.PE

Вот jpg вывода. Возможно, я потерял разрешение при кадрировании в поле.

pic output

...