# Asymptote: convert triple to array

How might I convert a triple of three real numbers into an array, or otherwise treat it as such. I can go from an array to a triple as:

\documentclass{article}
\usepackage{graphicx}
\usepackage{asymptote}
\begin{document}
\begin{asy}
triple[] p;
real[] b={1,2,3};

triple totriple(real[] x, int i){
return p[i] = (x,x,x);
}

totriple(b,1);
write(p);
\end{asy}
\end{document}


but the other direction eludes me, an attempt:

real[][] q;
triple v=(1,2,3);

real[] toarray(triple t, int i){
return q={t.x,t.y,t.z};
}


The goal is to visualize linear transformations in three dimensions. I have some triples that define the boundary of an object in 3-space, I want to convert them to arrays (so that I can multiply by a transformation matrix) and then back to triples for plotting. I may have missed some facility in Asymptote that accomplishes this out of the box, or an otherwise better way of doing this, so any and all suggestions would be welcome. Hopefully, this is on topic.

• Try this. real[][] q = new real; triple v=(1,2,3); void toarray(triple t, real[][] a, int i){ a[i] = t.x; a[i] = t.y; a[i] = t.z; } toarray(v,q,0); real[][] q2 = new real[]; void pushtoarray(triple t, real[][] a){ a.push(t.x); a.push(t.y); a.push(t.z); } pushtoarray(v,q2); pushtoarray(v + (2,4,6),q2); write(q2); – cjorssen May 2 '13 at 22:41
• @cjorssen That does the trick, could you add it as an answer so that I can accept it? – Scott H. May 3 '13 at 0:25
• now that you've added a complete MWE it is very obvious that this question is on topic- in its original form, it wasn't so obvious (to me, at least) :) – cmhughes May 3 '13 at 0:41
• @cmhughes That's fair, I probably should have just thrown the wrapper on in the beginning. It's just that I compile these as separate .asy files and not included in the body of a .tex file as the MWE would suggest. – Scott H. May 3 '13 at 1:38
• @cjorssen If/when you get the chance to write an answer, could you motivate what you've done above? I understand the assignments, it's the initializations that I'm having some trouble with, and why, for example, the output of write(q); differs from real[] x ={1,2,3}; write(x); – Scott H. May 3 '13 at 4:51

## 3 Answers

While an explicit function is of course a direct and portable way, the Asymptote offers an extension mechanism to cast one type to the other, to make the conversion automatic and clean:

triple operator cast(real[] r){
real[] t={0,0,0};
for(int i=0;i<min(r.length,3);++i)t[i]=r[i];
return (t,t,t);
}

real[] operator cast(triple tv){
return new real[]{tv.x,tv.y,tv.z};
}

triple[] p;
real[] b={1,2,3};

p=new real[]{};
p=new real[]{100};
p=new real[]{100,200};
p=new real[]{100,200,300};
p=new real[]{100,200,300,400,500};

p=b;

write("p=",p);

triple t=(9,8,7);

b[3:]=t;

write("b=",b);


The output is:

p=
0:      (0,0,0)
1:      (100,0,0)
2:      (100,200,0)
3:      (100,200,300)
4:      (100,200,300)
5:
6:      (1,2,3)
b=
0:      1
1:      2
2:      3
3:      9
4:      8
5:      7

• Thanks for this answer! Will be away from a computer for a bit, so can't try it out right away however. – Scott H. May 3 '13 at 16:59

I provide two functions to do the job. The first toarray needs the array to be dimensionned before the call and to know the index where you want the data (remember in asymptote, indexation begins at 0). The second pushtoarray adds the content of the triple at the end of the array (resizing it dynamically).

real[][] q = new real;
triple v=(1,2,3);
void toarray(triple t, real[][] a, int i){
a[i] = t.x;
a[i] = t.y;
a[i] = t.z;
}

toarray(v,q,0);
write(q)

real[][] q2 = new real[];

void pushtoarray(triple t, real[][] a){
a.push(t.x);
a.push(t.y);
a.push(t.z);
}

pushtoarray(v,q2);
pushtoarray(v + (2,4,6),q2);
write(q2);


This answer addresses your underlying goal of applying a transformation matrix to a triple, rather than your specific question.

First of all, here is a method that converts a linear map specified by a real 3x3 matrix to a transform3:

transform3 linMapToTransform3(real[][] matrix) {
transform3 T = copy(matrix);   // Produce a deep copy, so that the changes below won't affect the original matrix.
T.push(0);                  // Append a zero to the first row.
T.push(0);                  // Append a zero to the second row.
T.push(0);                  // Append a zero to the third row.
T.push(new real[]{0,0,0,1});   // Add a fourth row, which looks like {0,0,0,1}.
return T;
}


An file that tests the function above to make sure it works as expected (and, incidentally, illustrates conversion both ways between a triple and an array; see the fourth-from-last line of code):

import three;                    // Required for the type triple

// The function we want to test
transform3 linMapToTransform3(real[][] matrix) {
transform3 T = copy(matrix);   // Produce a deep copy, so that the changes below won't affect the original matrix.
T.push(0);                  // Append a zero to the first row.
T.push(0);                  // Append a zero to the second row.
T.push(0);                  // Append a zero to the third row.
T.push(new real[]{0,0,0,1});   // Add a fourth row, which looks like {0,0,0,1}.
return T;
}

// Define matrix multiplication
real[] operator *(real[][] T, real[] v) {
real[] toReturn = array(n=v.length, value=0);
for (int i = 0; i < T.length; ++i) {
for (int j = 0; j < v.length; ++j) {
toReturn[i] += T[i][j] * v[j];
}
}
return toReturn;
}

real[][] linTransform = new real;       // Initialize a 3x3 matrix
for (int i = 0; i < linTransform.length; ++i)
for (int j = 0; j < linTransform[i].length; ++j)
linTransform[i][j] = unitrand();          // Initialize each entry to a random number between 0 and 1

triple v = (unitrand(), unitrand(), unitrand());    //Initialize a vector to multiply by

real[] vec = new real[]{v.x, v.y, v.z};             //Convert the triple v to an array vdc

real[] product1 = linTransform * vec;                       //Product obtained by matrix multiplication
triple product2 = linMapToTransform3(linTransform) * v;     //Convert the linear map to a transform3 and apply it to the triple v

assert(product2 == (product1, product1, product1));    // If the two products do not represent the same thing, stop execution and display an error.


In Asymptote, one can often make this sort of thing automatic by defining a cast operator to convert one object to another that fits. Unfortunately, this is not possible here because transform3 is really just an alias for a two-dimensional real array as explained below, so you'd be trying to cast from real[][] to real[][], which does not work.

Explanation: There are a number of modules included in the plain module, which is loaded every time Asymptote is run. Among these modules are plain_prethree (it's included by plain_picture.asy, which is included by plain.asy).

The second line of code in the file plain_prethree.asy is

typedef real[][] transform3;


The typedef command in Asymptote is similar to \def or \let in TeX (but with the arguments in the opposite order). Thus, this line of code essentially defines the type transform3 as an alias for real[][], i.e., a two-dimensional array of real numbers. Testing and further consulting the source code reveals that in fact a transform3 is always expected to be a 4x4 matrix; if T is the transform3 specified by the matrix

{{a, b, c, d},
{e, f, g, h},
{i, j, k, l},
{0, 0, 0, 1}}


and v is a triple, then T*v is the triple obtained by multiplying this matrix on the right by the vertical vector {v.x, v.y, v.z, 1} and then dropping the last entry (which is necessarily 1). In other words, T*v is the triple

(a*v.x + b*v.y + c*v.z + d,
e*v.x + f*v.y + g*v.z + h,
i*v.x + j*v.y + k*v.z + l)

• Don't have time to read this right now but it looks very comprehensive, thanks! – Scott H. Jul 17 '13 at 17:59