The plane is tiled periodically, without rotation, by copies of a 5x5 square A and a 3x3 square B placed...
geometry/tiling/locate.in.pattern New
New entry, edited from material collected for the archive and not yet in a released version.
Problem
The plane is tiled periodically, without rotation, by copies of a 5x5
square A and a 3x3 square B placed together thus:
AAAAA
AAAAA
AAAAABBB
AAAAABBB
AAAAABBB
then repeating this pattern thus:
AAAAA
AAAAA
AAAAABBBAAAAA
AAAAAAAAAABBBAAAAA
AAAAAAAAAABBBAAAAA
AAAAABBBAAAAAAAAAA
AAAAAAAAAABBBAAAAAAAAAA
AAAAAAAAAABBBAAAAABBB
AAAAABBBAAAAAAAAAABBB
AAAAABBBAAAAAAAAAABBB
AAAAABBBAAAAABBB
AAAAABBB
AAAAABBB
[...]
My question is - given any point in space (X, Y) (integer values only),
what point within the pattern is it at, ie. which type of cell (A or B)
is it in and what are the offsets within that cell?
You can take any point of the pattern as the origin, I think of the
bottom left corner of an A cell as the origin.
eg. (0, 0) -> A(0, 0)
(5, 0) -> B(0, 0)
(5, 5) -> A(0, 2)
Other rules:
* the B cells are never greater than the A cells in X or Y, but may be
equal, or none existant (0*0) - which is the easy case anyway.
* I have used squares in this example, but the A and B cells can also
be rectangles, ie. I need a solution for any size of cells.
I have a computer code solution, but it involves loop's to transverse
the pattern to the point required. This is slow, and I would like to
find a way of calculating the position (using integer maths only) with
as few lines as possible.
-- Rapport Ltd <rapport@rapport.ltd.uk> (October 1997)
[ Editor: the first sentence is the editor's; the rest is the posting
"pattern problem", as quoted in the replies. ]
Show solution
Call the dimensions of the A cell (ax, ay) and those of the B cell
(bx, by). In your example ax=ay=5 and bx=by=3.
This tiling pattern is symmetric under the translations (ax, by) and
(bx, -ay). That is to say, if you are at (x,y) and you jump to
(x+ax, y+by) or (x+bx, y-ay) [Editor: "y-by" in the original],
everything will look the same. So the only trick is to determine how
many jumps one should make in either direction to reach the vicinity of
the origin.
P = (ax, by)
Q = (bx, -ay)
X = (x, y)
Let m be the greatest integer less than PX/P^2
(that is, (ax*x + by*y)/(ax*ax+by*by))
Likewise, let n be the greatest integer less than QX/Q^2
(Both of these operations are simple modulo statements if the
dimensions of the cells are integers, but they are not difficult in any
case.)
Now take X' = x - mP - nQ (that is, (x - m*ax - n*bx, y - m*by + n*ay))
X' will lie somewhere within a rectangle overlapping the "origin tile",
consisting of the A cell with it's lower left corner at the origin and
the B cell to it's right, and it will be in the same type of cell (and
the same position in the cell) as X.
To move X' into the origin tile, apply these rules (once):
if y<0, subtract Q
if x<0, add P
if y>ay, add Q
(Watch out, these rules require that the A cell is larger than the B
cell, otherwise they will have to be modified.)
Now X' lies in the origin tile, and the rest is trivial.
For example, take ax=ay=5, bx=by=3, (x,y) = (6,-32):
P = (5,3)
Q = (3,-5)
X = (6,-32)
PX/P^2 = -66/34 --> m=-2
QX/Q^2 = 178/34 --> n=5
X' = X + 2*P - 5*Q
= (6 + 2*5 - 5*3, -32 + 2*3 + 5*5)
= (1, -1)
y<0, so subtract (3,-5)
= (-2, 4)
x<0, so add (5,3)
= (3, 7)
y>5, so add (3, -5)
= (6, 2)
So X is in a B cell, in the middle near the top edge.
-- Horse Head <Horse_Head@nym.alias.net> (October 1997)
In the A=5*5, B=3*3 case, there are 25+9=34 distinct A and B position.
It's not too hard to show that
f(x,y) = (5 * x + 3 * y) mod 34
yields the following unique values for the A and B blocks:
12 17 22 27 32
9 14 19 24 29
6 11 16 21 26 31 2 7
3 8 13 18 23 28 33 4
0 5 10 15 20 25 30 1
This kind of attempt requires a lookup table, and also a search for
special multipliers if gcd(AX,BY) > 1 and gcd(AY,BX) > 1 where A,AY and
BX,BY are the dimensions of the A and B rectangles.
A much neater solution is to get equivalent x,y coords close to the
origin and then move them around into a standard region. The following
C code does the job. It shouldn't be hard to translate it into other
languages:
- FLOOR(x,y) is a bit like integer division x/y, but negative
numbers get rounded down, e.g. FLOOR(-3 10) = -1,
FLOOR(-10,10) = -1 FLOOR(-11,10) = -2 etc.
- NICEMOD is a normal modulo, returning numbers in the range
0..base-1, unlike C's "%" which can return negative numbers.
/*
** Solution code
*/
#define NICEMOD(n, base) ( ((n) % (base) + (base)) % (base) )
#define FLOOR(n, m) ( (n) >= 0 ? (n)/(m) : ((n)+1)/(m)-1 )
/*
** This routine prints the cell type for the given x,y using the
** given rectangle sizes.
*/
void evaluate(int ax, int ay, int bx, int by, int x, int y)
{
char block;
int rel_x;
int rel_y;
if (bx == 0 || by == 0)
{
block = 'A';
rel_x = NICEMOD(x, ax);
rel_y = NICEMOD(y, ay);
}
else
{
int p = bx * y + ay * x;
int q = ax * y - by * x;
int area = ax * ay + bx * by;
p = FLOOR(p, area);
q = FLOOR(q, area);
rel_x = x - ax * p + bx * q;
rel_y = y - by * p - ay * q;
if (rel_x < 0)
{
if (rel_y < ay - by)
{
rel_x = rel_x + ax;
rel_y = rel_y + by;
}
else if (rel_y < ay)
{
rel_x = rel_x + ax + bx;
rel_y = rel_y - ay + by;
}
else
{
rel_x = rel_x + bx;
rel_y = rel_y - ay;
}
}
else if (rel_y >= ay)
{
rel_x = rel_x + bx;
rel_y = rel_y - ay;
}
if (rel_x < ax)
block = 'A';
else
{
block = 'B';
rel_x = rel_x - ax;
}
}
#ifdef UGLY
printf("%c(%d,%d) ", block, rel_x, rel_y);
#else
if (rel_x == 0 && rel_y == 0)
putchar('+');
else if (rel_x == 0)
putchar('|');
else if (rel_y == 0)
putchar('-');
else
putchar(block);
#endif
}
main()
{
int ax, ay, bx, by, x0, y0, x1, y1;
while (scanf("%d %d %d %d %d %d %d %d\n",
&ax, &ay, &bx, &by, &x0, &y0, &x1, &y1) == 8)
{
int x, y;
/*
** This code loops through coords in the rectangle bounded
** by lower left (x0,y0) upper right and (x1,y1), printing
** the cell type for each coord.
*/
for (y = y1; y >= y0; y--)
{
for (x = x0; x <= x1; x++)
evaluate(ax, ay, bx, by, x, y);
printf("\n");
}
}
exit(0);
}
-- Fred Curtis <fred@zipper.zip.com.au> (October 1997)