Linear combination of two vectors

MATL, 48 bytes

tZyyX:UX>*Yat48-tt0>*3#fbbhb~2#fh-*s7M+'X'wZ}4$(

The background character is space. Input is a 2D char array with rows separated by semicolons. So the tests cases have respective inputs:

['    2'; ' 0   '; '   3 ']
[' 1 '; '   '; '1 0']
['0 1  1']
['  2 0 1   ']
['     3'; '      '; '   0  '; '      '; '      '; '2     ']
['90'; ' 8']

The output includes a significant amount of padding whitespace.

Try it online!


Python 3, 374 355 bytes

Not too refined python solution that is very generous with padding (uses maximum chessboard distance). Input is a single line where rows are separated with pipes | (although the algorithm can easily use anything not alphanumerical that isn't a newline or a EOF). Anything non alphanumeric or | works for input padding, output padding uses periods. Feedback and improvement from more seasoned python golfers is appreciated.

Edit: Some improvements thanks to @TheBikingViking. Also added even more margins since I wasn't quite generous enough with padding.

s=input()
l=[len(s),1+s.find('|')]['|'in s]
P=sorted([int(c),i%l,i//l]for i,c in enumerate(s)if c.isalnum())
L=X=Y=0
P[0][0]=-sum(p[0]for p in P)
for w,x,y in P:L=max(abs(x),abs(y),L);X+=x*w;Y+=y*w
P+=[['X',P[0][1]+X,P[0][2]+Y]]
P[0][0]=0
L=2*max(abs(X),abs(Y),L)
S=[2*L*["."]for _ in[0]*2*L]
for w,x,y in P:S[L+y][L+x]=str(w)
for s in S:print(''.join(s))

JavaScript, 534 528 502 bytes

n="indexOf"
J="join"
B=X=>X.split(O)
O='\n'
w=i[n](O)+1
h=B(i).length
Z=(X,Y,R)=>{C[R]+=X-1;return Array(X)[J](Y)}
C=[0,0,0]
G=(X,E,T,U,R)=>X>0&E>=0?Z(X+E+1+T,U,R):""
o=i[n]("0")
L=X=>Math.floor(X/(w-1))
l=L(o)
c=o%w
x=y=0
j=i
for(z="1";z<="9";z++){while(p=~j[n](z)){j=j.replace(z," ")
x+=~p%w-l
y+=L(~p)-c}}
I=B(i).map(X=>G(-x,-l,0," ",0)+X+G(x,l-w+2,0," ",2))
N=Z(I[0].length+1," ",2)
A=B(G(-y,-c,0,N+O,1)+I[J](O)+G(y,c-h,1,O+N,2))
M=y+c+C[1]
O=""
m=B(A[M])
m[x+l+C[0]/h]="x"
A[M]=m[J]("")
A[J]("\n")

Note that the padding is optimal. This program assumes that i contains the raw string, with the lines separated by \n characters. The padding is done with spaces, and the result character is a lowercase x.

This is my first attempt at code-golfing.

Technical stuff: - The program's size roughly doubled (and its complexity went up dramatically) to just take into account the result character, mostly because JavaScript strings are immutable.


Line by line explanation:

n="indexOf"
J="join"
B=X=>X.split(O)

I use those a lot, so storing them in strings saved me some space. You can see below that for the split function, I have simply created an alias; this is because I only needed one argument, the other one being constant. For indexOf and join, it would however have been longer.

O='\n'
w=i[n](O)+1
h=B(i).length

Nothing complicated here, I'm reading the width and height of the initial array. Note the use of i[n] to access indexOf, while split is handled differently.

Z=(X,Y,R)=>{C[R]+=X-1;return Array(X)[J](Y)}

This is getting interesting. This function basically creates concatenates J-1 times the string X and returns it. This is used to generate strings of spaces for the padding.

C=[0,0,0]

This array will contain the number of lines and columns added by the padding (off by a factor h in the first case). The last cell is a junk one, and prevents me from having an additional argument in the function below.

G=(X,E,T,U,R)=>X>0&E>=0?Z(X+E+1+T,U,R):""

This function alone handles the padding (both lines and columns); it determines, based on a coordinate of the result vector (X), and the number of lines/columns to generate (E), whether it is necessary to create one. the X+E+1+T is just a trick to save some space, U is the filling string (a space for columns, and an entire line for lines), and we'll come back to R. This function basically returns, in the case of a line, the padding required at the beginning or the end of said line, and, in the case of a column, it returns the padding lines required either before or after the original lines.

o=i[n]("0")
L=X=>Math.floor(X/(w-1))
l=L(o)
c=o%w

Here we read the position of the origin, and we retrieve its coordinates. L is a function to convert an index into a line number.

x=y=0
j=i
for(z="1";z<="9";z++){
    while(p=~j[n](z)){
        j=j.replace(z," ")
        x+=~p%w-l
        y+=L(~p)-c
    }
}

I added some whitespace to make this easier to read. What happens here is that for each possible number, we keep looking for it in the original string. The ~ trick is relatively common in Javascript; it is the bitwise NOT operator, but all that matters here is that ~-1==0, which allows me to test for the end of the loop. I then erase the character in the string (which is why I made a copy), this allowing me to continue the search as long as needed. I then add the coordinates of the vector to (x, y), using a simple substraction.

I=B(i).map(X=>G(-x,-l,0," ",0)+X+G(x,l-w+2,0," ",2))

I here split the original string into lines, and for each line, I call G which will generate the padding before and after the lines. The l-w+2 and so on come from a simple index calculation which allows me to test whether I need to add padding or not. For example, if x>0 and x+l-w+1>0, then (x+l-w+1)+1 spaces must be added after the line. The +x is being removed due to it being the first parameter, and the X+E+1+T used in the definition of G.

A similar thing is done for the first characters, and then for the columns. There is a lot of factorization here allowing me to use only one function. Note the last parameter; in the first case, I want to write to C[0] to be able to know later how many columns I added at the beginning of each line; this allows me to retrieve the final position of the result character. I do however not care about the columns added after the original line, which is why the second call to G writes to the junk cell C[2] which is unused.

N=Z(I[0].length+1," ",2)

Here I simply read the new length of the lines, and create a line of spaces from it. This will be used to create the vertical padding.

A=B(G(-y,-c,0,N+O,1)+I[J](O)+G(y,c-h,1,O+N,2))

This is exactly the same as two lines above. The only difference is writing to C[1] this time, and using the separators N+O and O+N. Remember that O is a newline, and N is a line of spaces. I then apply B on the result to split it again (I need to retrieve the line containing the result character to edit it).

M=y+c+C[1]

This is the vertical index of the resulting character.

O=""
m=B(A[M])
m[x+l+C[0]/h]="x"

Here I am forced to modify O to be able to split the appropriate line into an array of characters. This is because JavaScript strings are immutable; the only way to edit a string is to convert it into an array (which is what I'm doing here), edit at the right position, and join the string again. Also note the h factor, which is because the G function was called once per initial line.

A[M]=m[J]("")
A[J]("\n")

I finally replace the new string in the array, and join it again into a string. Woohoo!