Skip to navigation

Lander on the Acorn Archimedes

Maths (Geometry): GetMouseInPolarCoordinates (Part 1 of 2)

Name: GetMouseInPolarCoordinates (Part 1 of 2) [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Convert the mouse x- and y-coordinates into polar coordinates, starting by calculating the polar angle Deep dive: Flying by mouse
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * MoveAndDrawPlayer (Part 1 of 5) calls GetMouseInPolarCoordinates

This routine converts mouse coordinates (x, y) into polar coordinates, as in this example: .| R0 .´ | .´ | y .´R1 | +´-------+ x R1 is the angle and R0 is the distance.
Arguments: R0 The scaled-up mouse x-coordinate R1 The scaled-up mouse y-coordinate
Returns: R0 The distance of the polar coordinate R1 The angle of the polar coordinate
.GetMouseInPolarCoordinates \ In the following, let's call the mouse \ x- and y-coordinates xMouse and yMouse STMFD R13!, {R14} \ Store the return address on the stack MOV R3, #0 \ Set R3 = 0 CMP R0, #0 \ If the mouse x-coordinate in R0 is EORMI R3, R3, #%00000011 \ negative, negate R0 to get |xMouse| and RSBMI R0, R0, #0 \ flip bits 0 and 1 of R3 CMP R1, #0 \ If the mouse y-coordinate in R1 is EORMI R3, R3, #%00000111 \ negative, negate R1 to get |yMouse| and RSBMI R1, R1, #0 \ flip bits 0, 1 and 2 of R3 STMFD R13!, {R0-R1} \ Store |xMouse| and |yMouse| on the stack \ We now divide one coordinate by the other, \ with the smaller value as the numerator, \ so the result is between 0 and 1 \ \ We do this so we can take the arctan of \ the result to calculate the angle in our \ polar coordinate CMP R0, R1 \ If |xMouse| < |yMouse|, flip bit 0 of R3 EORLO R3, R3, #%00000001 \ so we can make sure the angle is in the \ correct quadrant later BHS pole2 \ If |xMouse| >= |yMouse|, jump to pole2 to \ calculate the division with |yMouse| as \ the numerator \ If we get here then |xMouse| < |yMouse| \ so we calculate the division with |xMouse| \ as the numerator \ We now calculate R2 = R0 / R1 using the \ shift-and-subtract division algorithm MOV R2, #0 \ Set R2 = 0 to contain the result MOV R14, #%10000000 \ Set bit 7 of R14 so we can shift it to the \ right in each iteration, using it as a \ counter .pole1 MOVS R0, R0, LSL #1 \ Shift R0 left, moving the top bit into the \ C flag CMPCC R0, R1 \ If we shifted a 0 out of the top of R0, \ test for a possible subtraction SUBCS R0, R0, R1 \ If we shifted a 1 out of the top of R0 or ORRCS R2, R2, R14 \ R0 >= R1, then do the subtraction: \ \ R0 = R0 - R1 \ \ and set the relevant bit in the result \ (i.e. apply the set bit in R14 to the \ result in R2) MOVS R14, R14, LSR #1 \ Shift R14 to the right, moving bit 0 into \ the C flag BCC pole1 \ Loop back until we shift the 1 out of the \ right end of R14 (after eight shifts) MOVS R2, R2, LSL #24 \ Scale up the result, so now we have: \ \ R2 = 2^24 * (|xMouse| / |yMouse|) B pole4 \ Jump to pole4 to skip the following .pole2 \ If we get here then |xMouse| >= |yMouse| \ so we calculate the division with |yMouse| \ as the numerator MOV R2, #0 \ Set R2 = R1 / R0 using the same algorithm MOV R14, #&80 \ as above \ \ This is the first part of the algorithm .pole3 MOVS R1, R1, LSL #1 \ This is the second part of the algorithm, CMPLO R1, R0 \ so now we have: SUBHS R1, R1, R0 \ ORRHS R2, R2, R14 \ R2 = 2^24 * (|yMouse| / |xMouse|) MOVS R14, R14, LSR #1 BLO pole3 MOVS R2, R2, LSL #24 .pole4 \ At this point we have calculated one of \ the following, with the smaller coordinate \ on the top of the division: \ \ R2 = 2^24 * (|xMouse| / |yMouse|) \ \ or: \ \ R2 = 2^24 * (|yMouse| / |xMouse|) \ \ So R2 is in the range 0 to 2^24 LDR R14, arctanTableAddr \ Set R14 to the address of the arctan \ lookup table BIC R2, R2, #&01800000 \ Clear bits 23 and 24 of R2 so when we \ shift R2 to the right by 23 places in the \ following lookup, the address is aligned \ to the words in the lookup table LDR R1, [R14, R2, LSR #23] \ Look up the arctan to get the following: \ \ R1 = ((2^31 - 1) / PI) * ATAN(R2 / 128) \ \ So R1 contains the smaller of the two \ angles in the triangle formed by xMouse \ and yMouse, like this (in this example, \ xMouse >= yMouse, though the ASCII art \ might not make that obvious): \ \ .| \ .´ | \ .´ | y \ .´R1 | \ +´-------+ \ x \ \ So we now have: \ \ R1 = arctan(|xMouse| / |yMouse|) \ \ or: \ \ R1 = arctan(|yMouse| / |xMouse|) TST R3, #%00000001 \ If bit 0 of R3 is set then: ADDEQ R1, R1, R3, LSL #29 \ ADDNE R3, R3, #1 \ R1 = R1 + R3 << 29 RSBNE R1, R1, R3, LSL #29 \ \ otherwise bit 0 of R3 is clear, so: \ \ R1 = (R3 + 1) << 29 - R1 \ \ So this moves the arctan angle into the \ correct circle quadrant, depending on the \ relative sizes and signs of xMouse and \ yMouse \ We now have the angle for our polar \ coordinate in R1, so next we need to \ calculate the distance