Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.postscript > #1998 > unrolled thread

setdash

Started byluser- -droog <mijoryx@yahoo.com>
First post2014-08-30 01:43 -0700
Last post2014-11-16 23:40 -0800
Articles 8 — 2 participants

Back to article view | Back to comp.lang.postscript


Contents

  setdash luser- -droog <mijoryx@yahoo.com> - 2014-08-30 01:43 -0700
    Re: setdash jdaw1 <jdawiseman@gmail.com> - 2014-08-30 02:51 -0700
      Re: setdash luser- -droog <mijoryx@yahoo.com> - 2014-08-30 20:40 -0700
        Re: setdash jdaw1 <jdawiseman@gmail.com> - 2014-09-01 02:39 -0700
        Re: setdash jdaw1 <jdawiseman@gmail.com> - 2014-09-02 02:06 -0700
          Re: setdash luser- -droog <mijoryx@yahoo.com> - 2014-09-04 16:51 -0700
            Re: setdash luser- -droog <mijoryx@yahoo.com> - 2014-09-16 03:37 -0700
              Re: setdash luser- -droog <mijoryx@yahoo.com> - 2014-11-16 23:40 -0800

#1998 — setdash

Fromluser- -droog <mijoryx@yahoo.com>
Date2014-08-30 01:43 -0700
Subjectsetdash
Message-ID<0410d6f7-a91f-4b56-a0f6-fd7f558676cd@googlegroups.com>
I need to implement setdash for my interpreter, but I'm 
so lazy..

Anybody know any tricks?

[toc] | [next] | [standalone]


#1999

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-08-30 02:51 -0700
Message-ID<f791cdde-84ed-484d-9f52-a242a52dca97@googlegroups.com>
In reply to#1998
No.

Is your interpreter converting PS to PDF, in which case the problem might be passed through. Or is it rendering the PS/PDF. Surely curves are rendered with an implicit flattenpath: do the same and the distance calculation becomes easy. Ensure that within the curve the dashes have 0 setlinecap 1 setlinejoin.

[toc] | [prev] | [next] | [standalone]


#2000

Fromluser- -droog <mijoryx@yahoo.com>
Date2014-08-30 20:40 -0700
Message-ID<6d617280-6964-4601-91c9-8600fa370bbe@googlegroups.com>
In reply to#1999
On Saturday, August 30, 2014 4:51:14 AM UTC-5, jdaw1 wrote:
> No.
> 
> 
> 
> Is your interpreter converting PS to PDF, in which case the problem might be passed through. Or is it rendering the PS/PDF. Surely curves are rendered with an implicit flattenpath: do the same and the distance calculation becomes easy. Ensure that within the curve the dashes have 0 setlinecap 1 setlinejoin.

It's rendering the image to a window. Although it can
also output to a file. But it reduces everything to polygon fills
at the postscript level.

Without bothering to look it up, I think applying dashing is the
first step of strokepath. And conceptually, it can work the same way.
So, this is the frame.

%apply dash parameters from graphics state to current path
%modifies current path
/dashpath {
    % ...
} def

I've seen blown-up examples without rounded cap, and I think 
it just follows the linejoin and linecap that are already set. 

I think you've found the crux with "distance calculation". 
It needs to traverse the path, measuring lengths against the
lengths in the dash array. It either needs to perform surgery
on the existing path data structure, or construct a new one as
it traverses. I remember Goswell's paper mentioned a funny edge
case with closepath, where the seam needed to be removed.

[toc] | [prev] | [next] | [standalone]


#2002

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-09-01 02:39 -0700
Message-ID<1cdbf719-c10f-4cec-b48e-99291e028b3e@googlegroups.com>
In reply to#2000
For computing the length you might use the method in 
http://steve.hollasch.net/cgindex/curves/cbezarclen.html 

Then render the painted parts of the dashes with a curveto: it's quite easy to compute the controls points for a subsection of a curve running from t1 to t2. (If needed I can post a file.)

[toc] | [prev] | [next] | [standalone]


#2004

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-09-02 02:06 -0700
Message-ID<608ec2aa-687e-4fc2-b96c-030d9399bc12@googlegroups.com>
In reply to#2000
I think you might need something like this. 

% x0 y0 x1 y1 x2 y2 x3 y3 WantedDistance Tolerance  FindDistanceInCurve  real bool
% If the bool is true, the real is the t value within cure
% if the bool is false, the real is the length of the whole curve
/FindDistanceInCurve
{
	...
} bind def  % /FindDistanceInCurve


Obviously lots missing. It will use a variation of the splitting algorithm in http://steve.hollasch.net/cgindex/curves/cbezarclen.html 

If within Tolerance of a straight line, then use the approximation in the linked article to compute estimated length. If length <= WantedDistance, return false and that length. Otherwise split curve. Repeat on first segment with Tolerance halved: if t found, then good. Obviously t's will need to be mapped from the sub-range into the whole. If t not found, subtract new-found length from target and repeat on second curve.

[toc] | [prev] | [next] | [standalone]


#2019

Fromluser- -droog <mijoryx@yahoo.com>
Date2014-09-04 16:51 -0700
Message-ID<35180be8-4d90-4a3c-9258-04bb99966999@googlegroups.com>
In reply to#2004
On Tuesday, September 2, 2014 4:06:22 AM UTC-5, jdaw1 wrote:
> I think you might need something like this. 
> 
> 
> % x0 y0 x1 y1 x2 y2 x3 y3 WantedDistance Tolerance  FindDistanceInCurve  real bool
> % If the bool is true, the real is the t value within cure
> % if the bool is false, the real is the length of the whole curve
> /FindDistanceInCurve
> {
> 	...
> } bind def  % /FindDistanceInCurve
> 
> 
> Obviously lots missing. It will use a variation of the splitting algorithm in http://steve.hollasch.net/cgindex/curves/cbezarclen.html 
> 
> If within Tolerance of a straight line, then use the approximation in the linked article to compute estimated length. If length <= WantedDistance, return false and that length. Otherwise split curve. Repeat on first segment with Tolerance halved: if t found, then good. Obviously t's will need to be mapped from the sub-range into the whole. If t not found, subtract new-found length from target and repeat on second curve.

On further thought, I think my task is simpler that all this. I can do the
dashing after converting curves to line segments. Then I just have to 
measure lines with the good old Pythagorean distance formula.

I still need to iterate through the path and compare lengths against the 
array of dash lengths, but the math should be much simpler than I feared.

[toc] | [prev] | [next] | [standalone]


#2054

Fromluser- -droog <mijoryx@yahoo.com>
Date2014-09-16 03:37 -0700
Message-ID<30d9f684-d1e3-40d8-8720-177653e9a8f5@googlegroups.com>
In reply to#2019
On Thursday, September 4, 2014 6:51:59 PM UTC-5, luser- -droog wrote:
> On Tuesday, September 2, 2014 4:06:22 AM UTC-5, jdaw1 wrote:
> 
> > I think you might need something like this. 
> 
> > 
> > 
> > % x0 y0 x1 y1 x2 y2 x3 y3 WantedDistance Tolerance  FindDistanceInCurve  real bool
> > % If the bool is true, the real is the t value within cure
> > % if the bool is false, the real is the length of the whole curve
> > /FindDistanceInCurve
> > {
> > 	...
> > } bind def  % /FindDistanceInCurve
> > 
> > 
> > Obviously lots missing. It will use a variation of the splitting algorithm in http://steve.hollasch.net/cgindex/curves/cbezarclen.html 
> > 
> > If within Tolerance of a straight line, then use the approximation in the linked article to compute estimated length. If length <= WantedDistance, return false and that length. Otherwise split curve. Repeat on first segment with Tolerance halved: if t found, then good. Obviously t's will need to be mapped from the sub-range into the whole. If t not found, subtract new-found length from target and repeat on second curve.
> 
> On further thought, I think my task is simpler that all this. I can do the
> dashing after converting curves to line segments. Then I just have to 
> measure lines with the good old Pythagorean distance formula.
> 
> I still need to iterate through the path and compare lengths against the 
> array of dash lengths, but the math should be much simpler than I feared.

Alright. It's written and (mostly) working. Ultimately the trickiest part
was keeping track of whether a point or vector was in user space or device 
space. The path data structure stores points in device space coordinates,
but the lengths in the dash array are in user space. So itransform first,
and transform before storing a new path element.

One remaining bug appears to happen only in arcs. The dash pattern is 
resetting in the middle of an arc. It probably means an extra 'moveto'
is in the path. But I think the bug is in arc/arcn, and not dashpath..

[toc] | [prev] | [next] | [standalone]


#2097

Fromluser- -droog <mijoryx@yahoo.com>
Date2014-11-16 23:40 -0800
Message-ID<553d3a58-3770-4959-b292-ac53ba256acc@googlegroups.com>
In reply to#2054
On Tuesday, September 16, 2014 5:37:23 AM UTC-5, luser- -droog wrote:
> On Thursday, September 4, 2014 6:51:59 PM UTC-5, luser- -droog wrote:
> > On Tuesday, September 2, 2014 4:06:22 AM UTC-5, jdaw1 wrote:
> > 
> > > I think you might need something like this. 
> > 
> > > 
> > > 
> > > % x0 y0 x1 y1 x2 y2 x3 y3 WantedDistance Tolerance  FindDistanceInCurve  real bool
> > > % If the bool is true, the real is the t value within cure
> > > % if the bool is false, the real is the length of the whole curve
> > > /FindDistanceInCurve
> > > {
> > > 	...
> > > } bind def  % /FindDistanceInCurve
> > > 
> > > 
> > > Obviously lots missing. It will use a variation of the splitting algorithm in http://steve.hollasch.net/cgindex/curves/cbezarclen.html 
> > > 
> > > If within Tolerance of a straight line, then use the approximation in the linked article to compute estimated length. If length <= WantedDistance, return false and that length. Otherwise split curve. Repeat on first segment with Tolerance halved: if t found, then good. Obviously t's will need to be mapped from the sub-range into the whole. If t not found, subtract new-found length from target and repeat on second curve.
> > 
> > On further thought, I think my task is simpler that all this. I can do the
> > dashing after converting curves to line segments. Then I just have to 
> > measure lines with the good old Pythagorean distance formula.
> > 
> > I still need to iterate through the path and compare lengths against the 
> > array of dash lengths, but the math should be much simpler than I feared.
> 
> Alright. It's written and (mostly) working. Ultimately the trickiest part
> was keeping track of whether a point or vector was in user space or device 
> space. The path data structure stores points in device space coordinates,
> but the lengths in the dash array are in user space. So itransform first,
> and transform before storing a new path element.
> 
> One remaining bug appears to happen only in arcs. The dash pattern is 
> resetting in the middle of an arc. It probably means an extra 'moveto'
> is in the path. But I think the bug is in arc/arcn, and not dashpath..

Buttoning-up this thread for the archive.

Dashing is implemented by the (obvious?) means of a
`dashpath` operator which replaces the current path
by its outline. As a special-effect, it may be called
prematurely to "show" the outline. But, as pre-
preemptively-caveat'ed in the PLRM, it's ugly-looking.

http://code.google.com/p/xpost/source/browse/data/path.ps#458

current version (warning long lines may break ill):

% PLRM 3ed, 667:
% The offset operand can be thought of as the "phase" of the dash pattern relative to
% the start of the path. It is interpreted as the distance into the dash pattern
% (measured in user space units) at which to start the pattern. Before beginning to
% stroke a path, the stroke operator cycles through the elements of array, adding up
% distances and alternating dashes and gaps as usual, but without generating any 
% output. When the accumulated distance reaches the value specified by offset, it 
% begins stroking from the starting point of the path, using the dash pattern from
% the point that has been reached. Each subpath of a path is treated independently;
% the dash pattern is restarted and the offset reapplied at the beginning of each
% subpath.

/initial_ipos {
    {
        pos dasharray ipos get lt { exit } if
        /pos pos dasharray ipos get sub def
        /ipos ipos 1 add dasharray length mod def
        /parity parity 1 add 2 mod def
    } loop
} def

/crossdashbound {
    {/ang/rad/data}{exch def}forall
    /partial dasharray ipos get pos sub def
    {
        %partial ang ravec parity 0 eq { rlineto }{ rmoveto } ifelse
        <</cmd parity 0 eq{ /line
            %(+)=
        }{ /move
            %(-)=
        } ifelse /data 
            cp aload pop
            partial ang ravec % x y dx dy
            3 2 roll add % x dx Y
            3 1 roll add exch % X Y
            2 copy 2 array astore /cp exch def
            transform 2 array astore >> cpath addtopath
        /rad rad partial sub def
        /ipos ipos 1 add dasharray length mod def
        /parity parity 1 add 2 mod def
        /partial dasharray ipos get def
        rad partial lt { exit } if
    } loop
    /pos rad def
    %rad ang ravec parity 0 eq { rlineto }{ rmoveto } ifelse
    <</cmd parity 0 eq{ /line
        %(+)=
    }{ /move
        %(-)=
    } ifelse /data 
        cp aload pop
        rad ang ravec % x y dx dy
        3 2 roll add % x dx Y
        3 1 roll add exch % X Y
        2 copy 2 array astore /cp exch def
        transform 2 array astore >> cpath addtopath
    data rad ang % pop pop pop
} def

/dashdict << % path i subpath_i j elem
    /move { 
        %(m)=
        %pstack()=
        /data get
        aload pop itransform 2 array astore
        %pstack()=
        /cp exch def
        parity 0 eq { % initially drawing, create initial move
            << /cmd /move /data cp aload pop transform 2 array astore >> cpath addtopath
        } if
    }
    /line {
        %(l)=
        /data get
        cp aload pop
        2 index aload pop
        itransform
        ptdiff % [] dx dy
        %idtransform
        magang % [] rad ang
        1 index pos add dasharray ipos get le { % same dash position
            %(.)=
            1 index pos add /pos exch def
            parity 0 eq { % if drawing, continue line  % [] rad ang
                2 index /cp 1 index aload pop itransform 2 array astore def
                << /cmd /line /data 5 4 roll >> cpath addtopath
            } if
        }{ % cross a dash pattern boundary
            crossdashbound
            {
            /partial dasharray ipos get pos sub def
            1 index pos add dasharray ipos get sub /pos exch def
            /ipos ipos 1 add dasharray length mod def
            /parity parity 1 add 2 mod def
            parity 0 eq { % begin new segment
                %(+)=
                partial exch ravec % [] rad dx dy
                %dtransform
                cp aload pop % [] rad dx dy x y
                3 2 roll add % dx x y+dy
                3 1 roll add exch % x+dx y+dy
                2 array astore /cp 1 index def
                << /cmd /move /data 5 4 roll aload pop transform 2 array astore >> cpath addtopath
                0
            }{ % finish partial segment
                %(-)=
                %pstack()=
                partial exch ravec % [] rad dx dy
                %dtransform
                cp aload pop 
                3 2 roll add
                3 1 roll add exch
                2 array astore /cp 1 index def
                << /cmd /line /data 5 4 roll aload pop transform 2 array astore >> cpath addtopath
                0
            } ifelse
            } pop
        } ifelse  % [] rad ang
        pop pop pop
    }
    /close { LINEPROC exec
        %(c)=
        closepath
    } dup 0 4 index put
    /curve { }
>> def

% apply dash parameters to current path
% modifies current path
/dashpath {
    10 dict begin
    currentdash % [] offset
    /dashoffset exch def
    /dasharray exch def
    /pos dashoffset def
    /ipos 0 def
    /parity 0 def

    dasharray length 0 ne {
        initial_ipos

        cpath length 0 gt {
            cpath newpath
            0 1 2 index length 1 sub { % path i
                2 copy get             % path i subpath_i
                10 dict begin
                /pos pos def
                /ipos ipos def
                /parity parity def
                0 1 2 index length 1 sub { % path i subpath_i j
                    2 copy get             % path i subpath_i j elem=subpath_i_j
                    dashdict 1 index /cmd get get exec % p i sp j
                    %(dp)=
                    %pstack()=
                    pop % p i sp
                } for
                pop pop % path
                end
            } for % path
            pop
        } if
    } if
    end
} def

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.postscript


csiph-web