DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK

# (gimpprint.info.gz) What is perfect weaving? Info Catalog (gimpprint.info.gz) Weaving collisions (gimpprint.info.gz) Weaving algorithms (gimpprint.info.gz) Oversampling
```
What makes a "perfect" weave?
-----------------------------

So what causes the perfect weave cases to be perfect, and the other
cases not to be?  In all the perfect cases above, S and J are
relatively prime (i.e. their greatest common divisor (GCD) is 1).  As
we mentioned above, S=6 and J=4 have a common factor, which causes the
overprinting.  Where S and J have a GCD of 1, they have no common
factor other than 1 and, as a result, no overprinting occurs.  If S and
J are not relatively prime, their common factor will cause overprinting.

We can work out the greatest common divisor of a pair of natural
numbers using Euler's algorithm:

* Swap them if necessary so that the larger one comes first: 24,   9

* Subtract the second number from the first:                 15,   9

* Repeat until the first number becomes smaller:              6,   9

* Swap the numbers again, so the larger one comes first:      9,   6

* Subtract again:                                             3,   6

* Swap:                                                       6,   3

* Subtract:                                                   3,   3

* And again:                                                  0,   3

* When one of the numbers becomes 0, the other number is the GCD of
the two numbers you started with.

These repeated subtractions can be done with C's `%' operator, so we
can write this in C as follows:

unsigned int
gcd(unsigned int x, unsigned int y)
{
if (y == 0)
return x;
while (x != 0) {
if (y > x)
swap (&x, &y);
x %= y;
}
return y;
}

`gcd(S,J)' will feature quite prominently in our weaving algorithm.

If 0 <= j < J, there should only be a single pair (p, j) for any
given row number.  If S and J are not relatively prime, this assumption
breaks down.  (For conciseness, let G=GCD(S,J).)

S=8,  J=6,  G=2:

0 *-------*-------*-------*-------*-------*
1       *-------*-------*-------*-------*-------*
2             *-------*-------*-------*-------*-------*
3                   *-------*-------*-------*-------*-------*
4                         ^-------^-------^-------*-------*-------*
5                               ^-------^-------^-------*-------*-------*

In this case, jets 0, 1 and 2 of pass p+4 collide with jets 3, 4 and
5 of pass p.

How can we calculate these numbers?  Suppose we were to print using
fewer jets, say J/G jets.  The greatest common divisor of J/G and S is
1, enabling a perfect weave.  But to get a perfect weave, we also have
to advance the paper by a factor of G less:

0 *-------*-------*       -       -       -
1    *-------*-------*       -       -       -
2       *-------*-------*       -       -       -
3          *-------*-------*       -       -       -
4             *-------*-------*       -       -       -
5                *-------*-------*       -       -       -

If we left the paper advance alone, we'd get a sparse weave; only one
row can be printed every G rows:

0 *-------*-------*       -       -       -
1       *-------*-------*       -       -       -
2             *-------*-------*       -       -       -
3                   *-------*-------*       -       -       -
4                         *-------*-------*       -       -       -
5                               *-------*-------*       -       -       -
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
These rows need filling in.

The rows that would have been printed by the jets we've now omitted
(shown as `-') are printed by other jets on later passes.

Let's analyse this.  Consider how a pass p could collide with pass
0.  Pass p starts at offset p*J.  Pass 0 prints at rows which are
multiples of S.  If p*J is exactly divisible by S, a collision has
occurred, unless p*J >= J*S (which will happen when we finish a pass
block).

So, we want to find p and q such that p*J=q*S and p is minimised.
Then p is the number of rows before a collision, and q is the number of
jets in pass 0 which are not involved in the collision.  To do this, we
find the lowest common multiple of J and S, which is L=J*S/G.  L/J is
the number of rows before a collision, and L/S is the number of jets in
the first pass not involved in the collision.

Thus, we see that the first J/G rows printed by a given pass are not
overprinted by any later pass.  However, the rest of the rows printed
by pass p are overprinted by the first J-(J/G) jets of pass p+(S/G).
We will use C to refer to S/G, the number of rows after which a
collision occurs.

Another example:

S=6,  J=9,  G=3,  C=S/G=2:

0 *-----*-----*-----*-----*-----*-----*-----*-----*
1          *-----*-----*-----*-----*-----*-----*-----*-----*
2                   ^-----^-----^-----^-----^-----^-----*-----*-----*
3                            ^-----^-----^-----^-----^-----^-----*-----*-----*
4                                     ^-----^-----^-----^-----^-----^-----*-----
5                                              ^-----^-----^-----^-----^-----^--
^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^
These rows need filling in.

In this case, the first J-(J/G) = 9-9/3 = 6 jets of pass p+(6/3)=p+2
collide with the last 6 jets of pass p.  Only one row in every G=2 rows
is printed by this weave.

S=9,  J=6,  G=3,  C=3:

0 *--------*--------*--------*--------*--------*
1       *--------*--------*--------*--------*--------*
2             *--------*--------*--------*--------*--------*
3                   ^--------^--------^--------^--------*--------*
4                         ^--------^--------^--------^--------*--------*
5                               ^--------^--------^--------^--------*--------*

Here, the first J-(J/G) = 6-6/3 = 4 jets of pass p+(9/3)=p+3 collide
with the last 4 jets of pass p.

Note that, in these overprinting cases, only rows divisible by G are
ever printed.  The other rows, those not divisible by G, are not
touched by this weave.

We can modify our weave pattern to avoid overprinting any rows and
simultaneously fill in the missing rows.  Instead of using J alone to
determine the start of each pass from the previous pass, we adjust the
starting position of some passes.  As mentioned before, we will divide
the page into pass blocks, with S passes in each block.  This ensures
that the first jet of the first pass in a block prints the row which
the Jth jet of the first pass of the previous block would have printed,

Looking back at an example of a perfect weave, we can divide it into
pass blocks:

S=7,  J=2,  G=1:

imaginary extra jet
|
0 *------*      *      <--start of pass block 0
1   *------*    |
2     *------*  |
3       *------*|
4         *-----|*
5           *---|--*
6             *-|----*
|
7               *------*  <--start of pass block 1
8                 *------*
9                   *------*

We can now calculate the start of a given pass by reference to its
pass block.  The first pass of pass block b always starts at row
(b*S*J).  The start row of each of the other passes in the block are
calculated using offsets from this row.

For the example above, there are 7 passes in each pass block, and
their offsets are 0, 2, 4, 6, 8, 10 and 12.  The next pass block is
offset S*J=14 rows from the start of the current pass block.

The simplest way to modify the "perfect" weave pattern to give a
correct weave in cases where G!=1 is to simply change any offsets which
would result in a collision, until the collision disappears.  Every
printed row in the weave, as we have shown it up to now, is separated
from each of its neighbouring printed rows by G blank rows.  We will
add an extra offset to each colliding pass in such a way that we push
the pass onto these otherwise blank rows.

We have seen that, unless G=1, the plain weave pattern results in
each pass colliding with the pass S/G passes before.  We will now
subdivide our pass block into subblocks, each consisting of B=S/G
passes.  There are therefore G subblocks in a pass block.

For each subblock, the passes in that subblock have a constant offset
added to them.  The offset is different for each subblock in a block.
There are many ways we can choose the offsets, but the simplest is to
make the offset equal to the subblock number (starting from 0).

Thus, the passes in the first subblock in each pass block remain at
the offsets we've already calculated from J.  The passes in the second
subblock each have 1 added to their offset, the passes in the third
subblock have 2 added, and so on.  Thus, the offset of pass p (numbered
relative to the start of its pass block) is p*J + floor(p/B).

This gives us a weave pattern looking like this:

S=6,  J=9,  G=3,  B=2:

0 *-----*-----*-----*-----*-----*-----*-----*-----*
1 ^        *-----*-----*-----*-----*-----*-----*-----*-----*
2 |              +-> *-----*-----*-----*-----*-----*-----*-----*-----*
3 |              |            *-----*-----*-----*-----*-----*-----*-----*-----*
4 |              |                  +-> *-----*-----*-----*-----*-----*-----*---
5 |              |                  |            *-----*-----*-----*-----*-----*
6 |              |                  |               +-> *-----*-----*-----*-----
7 |              |                  |               |            *-----*-----*--
|              |                  |             start of pass block 1
|              |                  |             (offset returns to 0)
|              |                  start of subblock 2 (offset 2 rows)
|              start of subblock 1 (following passes offset by 1 row)
start of passblock 0, subblock 0 (pass start calculated as p*J)

S=9,  J=6,  G=3,  B=3:

0 *--------*--------*--------*--------*--------*
1       *--------*--------*--------*--------*--------*
2             *--------*--------*--------*--------*--------*
3                    *--------*--------*--------*--------*--------*
4                          *--------*--------*--------*--------*--------*
5                                *--------*--------*--------*--------*--------*
6                                       *--------*--------*--------*--------*---
7                                             *--------*--------*--------*------
8                                                   *--------*--------*--------*
9                                                       *--------*--------*-----
10                                                  \---/     *--------*--------
11                                               small offset       *--------*--
12                                                                         *----

This method of choosing offsets for subblocks can result in an
occasional small offset (as shown above) between one pass and the next,
particularly when G is large compared to J.  For example:

S=8,  J=4,  G=4,  B=2:

0 *-------*-------*-------*
1     *-------*-------*-------*
2          *-------*-------*-------*
3              *-------*-------*-------*
4                   *-------*-------*-------*
5                       *-------*-------*-------*
6                            *-------*-------*-------*
7                                *-------*-------*-------*
8                                 *-------*-------*-------*
9                                \/   *-------*-------*-------*
very small offset!

We can plot the offset against the subblock number as follows:

subblock number
| offset
| |
| 0123
0 *
1  *
2   *
3    *
0 *
1  *
2   *
3    *

The discontinuity in this plot results in the small offset between
passes.

As we said at the beginning, we want the offsets from each pass to
the next to be as similar as possible.  We can fix this by calculating
the offset for a given subblock b as follows:

offset(b) = 2*b             , if b < ceiling(G/2)
= 2*(G-b)-1       , otherwise

We can visualise this as follows, for G=10:

0123456789
0 *
1   *
2     *
3       *
4         *
5          *
6        *
7      *
8    *
9  *
0 *
1   *
2     *
3       *
4         *
5          *
6        *
7      *
8    *
9  *

and for G=11:

1
01234567890
0 *
1   *
2     *
3       *
4         *
5           *
6          *
7        *
8      *
9    *
10  *
0 *
1   *
2     *
3       *
4         *
5           *
6          *
7        *
8      *
9    *
10  *

This gives a weave looking like this:

S=12,  J=6,  G=6,  B=2:

0 *-----------*-----------*-----------*-----------*-----------*
1       *-----------*-----------*-----------*-----------*-----------*
2               *-----------*-----------*-----------*-----------*-----------*
3                     *-----------*-----------*-----------*-----------*---------
4                             *-----------*-----------*-----------*-----------*-
5                                   *-----------*-----------*-----------*-------
6                                          *-----------*-----------*-----------*
7                                                *-----------*-----------*------
8                                                    *-----------*-----------*--
9                                                          *-----------*--------
10                                                             *-----------*----
11                                                                   *----------
12                                                                        *-----

This method ensures that the offset between passes is always in the
range [J-2,J+2].

(This might seem odd, but it occurs to me that a good weave pattern
might also make a good score for bell ringers.  When church bells are
rung, a list of "changes" are used.  For example, if 8 bells are being
used, they will, at first, be rung in order: 12345678.  If the first
change is for bells 5 and 6, the bells will then be rung in the order
12346578.  If the second change is 1 and 2, the next notes are 21346578.
After a long list of changes, the order the bells are rung in can become
quite complex.

For a group of bell-ringers to change the order of the notes, they
must each either delay their bell's next ring, hasten it, or keep it
the same as the time it takes to ring all the bells once.  The length
of time between each ring of a given bell can only be changed a little
each time, though; with an ink-jet weave pattern, we want the same to
apply to the distance between passes.)

Finally, knowing the number of jets J and their separation S, we can
calculate the starting row of any given pass p as follows:

passesperblock = S
passblock = floor(p / passesperblock)
offsetinpassblock = p - passblock * passesperblock
subblocksperblock = gcd(S, J)
passespersubblock = S / subblocksperblock
subpassblock = floor(offsetinpassblock / passespersubblock)
if subpassblock < ceiling(subblocksperblock/2)
subblockoffset = 2*subpassblock
else
subblockoffset = 2*(subblocksperblock-subpassblock)-1
startingrow = passblock * S * J + offsetinpassblock * J + subblockoffset

We can simplify this down to the following:

subblocksperblock = gcd(S, J)
subpassblock = floor((p % S) * subblocksperblock / S)
if subpassblock * 2 < subblocksperblock
subblockoffset = 2*subpassblock
else
subblockoffset = 2*(subblocksperblock-subpassblock)-1
startingrow = p * J + subblockoffset

So the row number of jet j of pass p is

subblocksperblock = gcd(S, J)

subblockoffset(p)
= 2*subpassblock       , if subpassblock * 2 < subblocksperblock
= 2*(subblocksperblock-subpassblock)-1      , otherwise
where
subpassblock = floor((p % S) * subblocksperblock / S)

row(j, p) = p * J + subblockoffset(p) + j * S

Together with the inequality 0 <= j < J, we can use this definition
in reverse to calculate the pass number containing a given row, r.
Working out the inverse definition involves a little guesswork, but one
possible result is as follows.  Given a row, r, which is known to be
the first row of a pass, we can calculate the pass number as follows:

subblocksperblock = gcd(S, J)
subblockoffset = r % subblocksperblock
pass = (r - subblockoffset) / J

If G==1, we can determine the pass number with this algorithm:

offset = r % J
pass = (r - offset) / J
while (offset % S != 0)
{
pass--
offset += J
}
jet = offset / S

Generalising, we come up with this algorithm.  Given r, S and J:

G = gcd(S, J)
passespersubblock = S/G
subblockoffset = r % G
subpassblock = subblockoffset / 2  , if subblockoffset % 2 == 0
= G - (subblockoffset+1)/2    , otherwise
baserow = r - subblockoffset - (subpassblock * passespersubblock * J)
offset = baserow % J
pass = (baserow - offset) / J
while (offset % S != 0)
{
offset += J
pass -= 1
}
subblockretreat = floor(pass / passespersubblock) % G
pass -= subblockretreat * passespersubblock
pass += subpassblock * passespersubblock
jet = (r - subblockoffset - pass * J) / S

Let's look at some examples of imperfect but correct weave patterns:

S=6,  J=4,  GCD=2,
passesperblock=S=6,
passespersubblock=S/G=6/2=3:

0 *-----*-----*-----*
1     *-----*-----*-----*
2         *-----*-----*-----*
3              *-----*-----*-----*
4                  *-----*-----*-----*
5                      *-----*-----*-----*
6                         *-----*-----*-----*
7                             *-----*-----*-----*
8                                 *-----*-----*-----*
9                                      *-----*-----*-----*
10                                         *-----*-----*-----*
11                                             *-----*-----*-----*
12                                                *-----*-----*-----*
13                                                    *-----*-----*-----*
14                                                        *-----*-----*-----*
15                                                             *-----*-----*----
16                                                                 *-----*-----*
17                                                                     *-----*--
18                                                                        *-----
19                                                                            *-

S=8,  J=6,  G=2,
passesperblock=S=8,
passespersubblock=S/G=8/2=4:

0 *-------*-------*-------*-------*-------*
1       *-------*-------*-------*-------*-------*
2             *-------*-------*-------*-------*-------*
3                   *-------*-------*-------*-------*-------*
4                          *-------*-------*-------*-------*-------*
5                                *-------*-------*-------*-------*-------*
6                                      *-------*-------*-------*-------*-------*
7                                            *-------*-------*-------*-------*--
8                                                 *-------*-------*-------*-----
9                                                       *-------*-------*-------
10                                                            *-------*-------*-
11                                                                  *-------*---
12                                                                         *----

S=6,  J=12,  G=6,
passesperblock=S=6,
passespersubblock=S/G=6/6=1:

0 *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*
1               *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----*---
2                             *-----*-----*-----*-----*-----*-----*-----*-----*-
3                                          *-----*-----*-----*-----*-----*-----*
4                                                    *-----*-----*-----*-----*--
5                                                              *-----*-----*----
6                                                                         *-----

We have now solved the basic weaving problem.  There are two further
refinements we need to consider: oversampling, and filling in the
missing rows at the start of the weave.

``` Info Catalog (gimpprint.info.gz) Weaving collisions (gimpprint.info.gz) Weaving algorithms (gimpprint.info.gz) Oversampling
automatically generated byinfo2html