Как я могу программно генерировать изображения диаграммы Венна с метками поверх изображения?

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

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

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

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

Буду признателен за любые ссылки/указатели.


person klochner    schedule 19.09.2011    source источник
comment
Генерация инкапсулированного PostScript была бы хорошей ставкой. Его не так уж сложно использовать.   -  person Tom Zych    schedule 20.09.2011
comment
Любые предлагаемые библиотеки для создания Venn в формате eps?   -  person klochner    schedule 20.09.2011
comment
Я уверен, что они существуют, но я всегда просто кодировал их вручную. Это отличный язык.   -  person Tom Zych    schedule 20.09.2011
comment
какая у вас почасовая оплата за консультации? ;-П   -  person klochner    schedule 20.09.2011
comment
Будет работать для голосов :)   -  person Tom Zych    schedule 20.09.2011
comment
Спасибо за согласие. Это сообщение может быть полезно другим — чтобы упростить поиск, можно ли заменить один из выбранных вами тегов на [postscript]?   -  person Tom Zych    schedule 20.09.2011
comment
Рассматривали ли вы pic?   -  person luser droog    schedule 21.09.2011


Ответы (4)


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

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
person Tom Zych    schedule 20.09.2011
comment
Спасибо, Том - проголосовал, и я приму, если вы дадите мне 3 круга в eps. (Я могу понять это, но если вы не возражаете...) - person klochner; 20.09.2011
comment
Я сделаю это, но отложите принятие на пару дней. я любитель; если на него нет ответа, кто-то другой может предложить лучшее решение. - person Tom Zych; 20.09.2011
comment
Если аргументы используются только один или два раза, обычно предпочтительнее использовать манипулирование стеком, а не временный словарь /CenterText { dup stringwidth pop -.5 mul 4 -1 roll add 3 -1 roll moveto show } bind def Кроме того, newpath глупо, show сделает это за вас. - person luser droog; 21.09.2011
comment
Я думаю, что вы на самом деле делаете 4 итерации этого цикла for. Но поскольку 4-й такой же, как и 1-й, никакого вреда не будет. - person luser droog; 24.09.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!

Изменить:

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

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

.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

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

Изменить:

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

.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 вывода. Возможно, я потерял некоторое разрешение при обрезке до коробки.

вывод изображения

person luser droog    schedule 21.09.2011

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

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

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

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

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

4-пятиугольная диаграмма Эйлера

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

Для диаграммы с двумя кругами лучшее размещение, которое я могу найти, определяется пересечением радиусов от центра диаграммы через центры кругов к краям с определяющими кругами, расположенными в центрах кругов. 2-круговая диаграмма Эйлера с радиусами и повторяющимися определяющими окружностями

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

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

Редактировать: я думаю, что почти слизал это. Эта программа содержит только те части предыдущей программы, которые необходимы для создания 2-х и 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- и 3-диаграммы Венна с метками

person luser droog    schedule 23.09.2011

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

Намного проще, чем вручную писать ps:

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

person A T    schedule 11.10.2011