Повернуть на север - PullRequest
       2

Повернуть на север

3 голосов
/ 18 декабря 2011

После выполнения сложной серии поворотов и перемещений я хочу вернуть текущее направление на «север», указывающее вверху страницы.Как я могу это сделать?

Очевидный ответ - отслеживать, в каком направлении я указываю каждый раз, когда я переводю, но это кажется большой работой.Я хочу что-то вроде «0 rotateto», которое оставляет меня в текущем местоположении, но указывает на абсолютную верхнюю часть страницы;аналогично, «90 rotateto» будет указывать вправо.

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

Моя цель - реализовать черепаху в стиле логотипа.Две из команд, которые распознает черепаха, это setpos (которая перемещается в абсолютную позицию) и setorientation (которая вращается в абсолютном направлении).Я пытаюсь выяснить, как реализовать эти команды в PostScript.

РЕДАКТИРОВАТЬ 1:

Спасибо, luser droog, но что-то все еще не так.Моя библиотека включает команды turtle, показанные ниже, с их эквивалентами PostScript;rotateto и setpos - определения, которые вы дали ранее, is_penup - глобальная переменная, изначально установленная в true, и я еще не учел pos и ориентацию, которые сообщают текущую абсолютную позицию xy и ориентацию черепахи:

init             -- %!
                    /rotateto { ... } bind def
                    /setpos { ... } def
                    newpath
                    306 396 moveto % center 8.5x11 portrait
                    0 setgray 2 setlinewidth
penup            -- % set global variable is_penup to true
pendown          -- % set global variable is_penup to false
forward n        -- if is_penup then 0 n rmoveto currentpoint translate
                                else 0 n rlineto currentpoint translate
backward n       -- if is_penup then 0 n neg rmoveto currentpoint translate
                                else 0 n neg rlineto currentpoint translate
right n          -- n neg rotate
left n           -- n rotate
setpos x y       -- x y setpos
setorientation n -- n rotateto
done             -- stroke newpage
pos              -- get current absolute x y position
orientation      -- get current absolute orientation

Пример, который рисует два квадрата, показан ниже;квадратная команда записывает четыре строки длиной 50, за которыми следует поворот вправо на 90 градусов:

init
pendown
setpos 100 100
square 50
setpos 400 400
right 45
square 25
done

Но это не сработает.Ни одна из команд setpos не соблюдается.Есть два квадрата, и второй квадрат наклонен на 45 градусов, но оба начинаются в центре страницы.Боюсь, что каждый раз, когда я говорю, что currentpoint translate мешает тому, что вы делаете в вашей команде setpos.

Можете ли вы предложить какие-либо предложения?

EDIT 2:

Я решил не беспокоиться о возвращении текущей позиции и ориентации в Схему;это слишком много работы для моей текущей цели, хотя это может пригодиться когда-нибудь в будущем.Окончательная версия кода моей Схемы показана ниже.Send - это обычная утилита, turtle-init определяет библиотеку turtle в PostScript, далее следует остальная часть библиотеки Scheme turtle, затем пример программы, которая рисует два квадрата.Все работает нормально.

(define (send x . xs)
  (cond ((null? xs) (display x) (newline))
  (else (display x) (display " ") (apply send xs))))

(define (turtle-init)
  (for-each send '(
    "%!"
    "/defmat matrix defaultmatrix def"
    "/fix { currentpoint translate } def"
    "/rotateto { matrix rotate"
    "    dup 4 matrix currentmatrix 4 2 getinterval"
    "    putinterval setmatrix } def"
    "/setpos { defmat transform itransform moveto fix } def"
    "/left { rotate } def"
    "/right { neg rotate } def"
    "/is-pendown false def"
    "/penup { /is-pendown false def } def"
    "/pendown { /is-pendown true def } def"
    "/done { stroke showpage } def"
    "/init { initgraphics 306 396 moveto fix 0 setgray 2 setlinewidth } def"
    "/forward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def"
    "/backward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def")))
(define (turtle-penup) (send "penup"))
(define (turtle-pendown) (send "pendown"))
(define (turtle-forward n) (send n "forward"))
(define (turtle-backward n) (send n "backward"))
(define (turtle-right n) (send n "right"))
(define (turtle-left n) (send n "left"))
(define (turtle-setpos x y) (send x y "setpos"))
(define (turtle-setorientation n) (send n "rotateto"))
(define (turtle-done) (send "stroke showpage"))

(define (square n)
  (do ((i 4 (- i 1))) ((zero? i))
    (turtle-forward n) (turtle-right 90)))

(define (squares)
  (turtle-init)
  (turtle-pendown)
  (turtle-setpos 100 100)
  (square 50)
  (turtle-setpos 400 400)
  (turtle-right 45)
  (square 25)
  (turtle-done))

(with-output-to-file "squares.ps"
  (lambda () (squares)))

Это все появится в моем блоге некоторое время в январе;Я еще не уверен в точной дате.

Спасибо!

РЕДАКТИРОВАТЬ 3:

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

/setpos { defmat transform itransform is-pendown
    { lineto } { moveto } ifelse fix } def

Я также изменил turtle-init так, что он фактически вызывает init, а не просто определяет его.Не очень полезно, если вы никогда не звоните.

Ответы [ 4 ]

3 голосов
/ 06 апреля 2012

Месяцы спустя ...

Мы все идиоты! Безусловно, является «абсолютной» системой координат: пространство устройства! Конечно, операторы moveto и currentpoint взаимодействуют с вашей программой в CTM-относительных координатах, вы можете вручную «отменить» преобразование, чтобы «занять абсолютную позицию» и «повторить» его позже, чтобы преобразовать в CTM-относительные координаты.

В этом примере используется OO-Turtle и набор макро-расширений для выполнения системы Lindenmayer со встроенными преобразованиями (кружева королевы Анны).

Queen Anne's Lace

И магические линии:

     % ...
     currentpoint transform   % save "absolute" position
     % ...
     itransform translate
     0 0 moveto     % return to saved position
     % ...

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

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

%!
%linden.ps

%one Iteration of macro-expansion
% traverses an array, replacing tokens
% defined in the dictionary P,
% and constructs a new array
/I { % O  I  O'
    mark exch {
        P exch 2 copy known {
            get aload pop
        }{
            exch pop
        } ifelse
    } forall counttomark array astore exch pop cvx
} def
%Generate nth expansion of O
/G { % n  G  O'^n
    /O load exch
    { I } repeat
} def

% n x y  x  -
% moveto x y, generate nth expansion of O, stroke, grestore
/x { moveto gsave G exec stroke grestore } def


/Turtle <<
    /forward {
        dup Turtle /angle get cos mul
        exch Turtle /angle get sin mul
        2 copy
        Turtle /pen? get { lineto }{ moveto }ifelse
        translate
    }
    /angle 90
    /pen? false
    /right { Turtle /angle 2 copy get 4 3 roll sub put }
    /left { Turtle /angle 2 copy get 4 3 roll add put }
    >> def
/send { get exec } def
/ini { 300 400 2 copy moveto translate } def

<< %shortcuts
    /F { R Turtle /forward send }
    /+ { T Turtle /right send }
    /- { T Turtle /left send }
>> begin

ini

{ %branching stems
/V << % description of the Lindenmeyer System
    /#0 {F leaf}
    /#1 {F}

    /O { #0 } % Original
    /P << % Productions
        /#0 { #1 [ - #0 ] + #0 }
        /#1 { #1 #1 }
        >>
    /R 10
    /T 45
    /leaf { 0 0 R 2 mul 0 180 arc 0 0 moveto }
>> def
V begin
0 -300 translate
.5 .5 scale
Turtle /pen? true put

gsave
    %"Special" Definitions
    % The brackets in the procedure resulting
    % from the expansion of /O are not used to
    % construct arrays, but to save and reset
    % the position and angle of the Turtle.
    ([) {
        %matrix currentmatrix 4 2 getinterval aload pop
        currentpoint transform
        Turtle /angle get
    } def
    (]) {
        Turtle /angle 3 2 roll put
        %matrix currentmatrix % Tx Ty M
        %dup 4 5 4 roll put % Ty M
        %dup 5 4 3 roll put % M
        %setmatrix
        itransform translate
        0 0 moveto
    } def
    .5 .5 scale
    8 0 0 x % execute 8-level-deep expansion of /O in /V
grestore

end showpage
} exec
3 голосов
/ 18 декабря 2011

Ну, для начала, ось Y по умолчанию указывает на север. Таким образом, любые нормальные перемещения и повороты могут быть сброшены с помощью matrix defaultmatrix setmatrix. Вы можете сбросить масштабирование и вращение, не изменяя перевод чем-то вроде matrix currentmatrix dup 0 matrix defaultmatrix 0 4 getinterval putinterval setmatrix.

Если вы можете игнорировать масштабирование, вы можете сделать rotateto (при условии, что текущая позиция является источником пользовательского пространства) следующим образом:

/rotateto { % angle  rotateto  -
    matrix rotate % create a rotation matrix
    dup 4
    matrix currentmatrix 4 2 getinterval %get the current translation
    putinterval setmatrix % put translation into rot. matrix and install
} bind def

Чтобы установить абсолютную позицию, вам нужно выполнить несколько преобразований.

Обычно любые указанные координаты относятся к пространству пользователя. Это означает, что они умножаются на Матрицу преобразования тока перед тем, как вступить в силу. Установить «абсолютную» позицию означает интерпретировать координаты как ссылку на «абсолютное пространство». Единственное такое привилегированное пространство - Матрица по умолчанию.

/setpos { % x-abs y-abs  setpos  -
    matrix defaultmatrix transform % x' y'  %to device space
    itransform % x y  %back to current user space
    translate
} def

Редактировать: Это хитрая штука! Я смог заставить ваш тест работать с этим набором процедур.

%!
/defmat matrix defaultmatrix def
/fix { currentpoint translate } def
/rotateto { matrix rotate
    dup 4 matrix currentmatrix 4 2 getinterval
    putinterval setmatrix } def
/setpos { defmat transform itransform moveto fix } def
/left { rotate } def
/right { neg rotate } def
/is-pendown false def
/penup { /is-pendown false def } def
/pendown { /is-pendown true def } def
/done { stroke showpage } def
/init { initgraphics 306 396 moveto fix 0 setgray 2 setlinewidth } def
/pos { matrix currentmatrix 4 2 getinterval {} forall } def
/forward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def
/backward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def

/square { 4 { dup forward 90 right } repeat pop } def
init
pendown
100 100 setpos
50 square
400 400 setpos
45 right
25 square
done

Одна вещь, которая меня взволновала, это запоминание currentpoint translate после каждого хода (включая начальный ход).

Для orientation вам придется «интерпретировать» матрицу. Мне нужно подумать над этим, прежде чем делать предложения. Для начала помните, что матрица вращения выглядит как [cosA sinA -sinA cosA 0 0].

2 голосов
/ 24 декабря 2011

Похищение идеи Кена и дальнейшее понимание того, что вы пытаетесь сделать (из вашего блога ), Вот псевдо-ОО постскриптумная черепаха.

%!
/Turtle <<
    /forward {
        dup Turtle /angle get cos mul
        exch Turtle /angle get sin mul
        2 copy
        Turtle /pen? get { lineto }{ moveto }ifelse
        translate
    }
    /angle 90
    /pen? false
    /right { Turtle /angle 2 copy get 4 3 roll sub put }
    /left { Turtle /angle 2 copy get 4 3 roll add put }
    >> def
/send { get exec } def
/ini { 300 400 2 copy moveto translate } def

<< %shortcuts
    /F { 100 Turtle /forward send }
    /+ { 90 Turtle /right send }
    /- { 90 Turtle /left send }
>> begin

ini
Turtle /pen? true put
F + F + F + F
F + F + F + F
F + F + F + F
F + F + F + F
stroke %draws a four-square
showpage
2 голосов
/ 18 декабря 2011

PostScript 'moveto' - это абсолютное перемещение, а не относительное. В PostScript, если вы хотите относительное перемещение, вы используете rmoveto. Тем не менее, по сути, нет прямого доступа к пространству устройства. PostScript имеет два пространства: теоретически бесконечно точное пользовательское пространство и пространство устройства, которое фактически печатается и имеет разрешение устройства вывода. Current Transformation Matr4ix (CTM) отображает пространство пользователя в пространство устройства.

Вы не можете получить доступ к пространству устройства напрямую, все операции PostScript выполняются через CTM. Таким образом, нет способа обратиться к абсолютной точке в пространстве устройства, и поэтому не существует «абсолютного перемещения» в том смысле, в котором вы просите, все точки в пространстве пользователя преобразуются через CTM, чтобы добраться до пространство устройства.

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

Если вы вращаете CTM, то да, это действительно ваше дело, чтобы отслеживать это. Вы можете сбросить CTM несколькими способами. Вы можете gsave / grestore вокруг серии операций. Обратите внимание, что это сбросит все, включая текущую точку и путь. Если вы хотите сохранить текущую точку, то "gsave ..... currentpoint grestore moveto" сделает это.

В качестве примечаний luser droog вы можете использовать стандартную матрицу setmatrix или вычислить собственную матрицу и использовать setmatrix, например, 'currentmatrix invertmatrix concat setmatrix'.

Но в целом вы поворачиваете CTM только для специальных эффектов, таких как резка или рисование изображений и т. Д., И обычная техника PostScript заключается в сохранении / восстановлении таких последовательностей.

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

...