Reputation: 5045
I am making something in Java (particularly Android) that displays something like the following:
The user can drag the corners of the box, and as expected, the dashed lines should move accordingly, always having 3 horizontal lines (one half way, one 25%, and one 75% between the top and the bottom) and 3 vertical lines. However, if either the left or right edge is infinity (which it is when the user first sees the screen), it looks something like this:
How can I change my code so that the infinite slope edges are accounted for and still show the desired screen? Here is my code for detecting the positions of where the dashed lines should be, their slopes, and finally them drawn to screen.
//get edge slopes to calculate dashed lines
//c1X is the top left corner X position, c2X is for the top right corner, etc.
float topLineSlope = (c2Y - c1Y)/(c2X - c1X);
float rightLineSlope = (c3Y - c2Y)/(c3X - c2X);
float bottomLineSlope = (c4Y - c3Y)/(c4X - c3X);
float leftLineSlope = (c1Y - c4Y)/(c1X - c4X);
//b in y=mx+b
float topLineB = c1Y - (topLineSlope * c1X);
float rightLineB = c2Y - (rightLineSlope * c2X);
float bottomLineB = c3Y - (bottomLineSlope * c3X);
float leftLineB = c4Y - (leftLineSlope * c4X);
//final dashed line coordinates
float topLineMiddleX = (c1X + c2X) / 2.0f;
float topLineMiddleY = topLineSlope * topLineMiddleX + topLineB;
float bottomLineMiddleX = (c3X + c4X) / 2.0f;
float bottomLineMiddleY = bottomLineSlope * bottomLineMiddleX + bottomLineB;
float leftLineMiddleX = (c4X + c1X) / 2.0f;
float leftLineMiddleY = leftLineSlope * leftLineMiddleX + leftLineB;
float rightLineMiddleX = (c2X + c3X) / 2.0f;
float rightLineMiddleY = rightLineSlope * rightLineMiddleX + rightLineB;
float topLineLeftX = (c1X + topLineMiddleX) / 2.0f;
float topLineLeftY = topLineSlope * topLineLeftX + topLineB;
float bottomLineLeftX = (c4X + bottomLineMiddleX) / 2.0f;
float bottomLineLeftY = bottomLineSlope * bottomLineLeftX + bottomLineB;
float topLineRightX = (topLineMiddleX + c2X) / 2.0f;
float topLineRightY = topLineSlope * topLineRightX + topLineB;
float bottomLineRightX = (c3X + bottomLineMiddleX) / 2.0f;
float bottomLineRightY = bottomLineSlope * bottomLineRightX + bottomLineB;
float leftLineTopX = (c1X + leftLineMiddleX) / 2.0f;
float leftLineTopY = leftLineSlope * leftLineTopX + leftLineB;
float rightLineTopX = (c2X + rightLineMiddleX) / 2.0f;
float rightLineTopY = rightLineSlope * rightLineTopX + rightLineB;
float leftLineBottomX = (leftLineMiddleX + c4X) / 2.0f;
float leftLineBottomY = leftLineSlope * leftLineBottomX + leftLineB;
float rightLineBottomX = (c3X + rightLineMiddleX) / 2.0f;
float rightLineBottomY = rightLineSlope * rightLineBottomX + rightLineB;
canvas.drawLine(topLineMiddleX, topLineMiddleY, bottomLineMiddleX, bottomLineMiddleY, dashedLine);
canvas.drawLine(leftLineMiddleX, leftLineMiddleY, rightLineMiddleX, rightLineMiddleY, dashedLine);
canvas.drawLine(topLineLeftX, topLineLeftY, bottomLineLeftX, bottomLineLeftY, dashedLine);
canvas.drawLine(topLineRightX, topLineRightY, bottomLineRightX, bottomLineRightY, dashedLine);
canvas.drawLine(leftLineTopX, leftLineTopY, rightLineTopX, rightLineTopY, dashedLine);
canvas.drawLine(leftLineBottomX, leftLineBottomY, rightLineBottomX, rightLineBottomY, dashedLine);
Upvotes: 1
Views: 1347
Reputation: 881193
If it's always a rectangle where the bottom side is perfectly horizontal, you shouldn't need to use slopes at all, you simply draw a horizontal line from left to right at the two internal levels, such as:
bottom + (top - bottom) / 3
bottom + 2 * (top - bottom) / 3
And ditto for vertical lines top to bottom.
That's basically just performing the following operations, assuming bottom-left is the closest point to the origin:
hspread = (right - left) / 3
line (top, left + hspread) - (bottom, left + hspread)
line (top, left + 2 * hspread) - (bottom, left + 2 * hspread)
vspread = (top - bottom) / 3
line (bottom + vspread, left) - (bottom + vspread, right)
line (bottom + 2 * vspread, left) - (bottom + 2 * vspread, right)
If the corners can move independently (i.e., it doesn't have to be rectangular), you still don't need to involve yourself with slopes.
What you need to do then is to find the points along each side that equate to 1/3 and 2/3 distance along the line, and join them. For a line (x1,y1)-(x2,y2)
, you can find the point 4/5
along it simply by using:
x = x1 + (x2-x1) * 4/5
y = y1 + (y2-y1) * 4/5
For example, if the shape is thus:
(x1,y1) (x2,y2)
___--+
+----' \
| \
| \
| \
| \
+----------------+
(x3,y3) (x4,y4)
:
:
:......
(0,0)
(apologies for the "graphics"), the topmost horizontal line can be drawn as:
line (x3 + (x1-x3) * 2/3, y3 + (y1-y3) * 2/3)
- (x4 + (x2-x4) * 2/3, y4 + (y2-y4) * 2/3)
This is the more generalised form of an earlier answer of mine to find the midpoint of a given line.
So, in both cases, all you need is a line drawing primitive based on start and end points (which you already have), with no messing about with potentially infinite slopes.
Upvotes: 0
Reputation: 3930
Without knowing more about the program I can't definitively answer. That said, I have some ideas for things you can try, but I have questions related to each:
I find it strange that the left and right edges are equal to infinity when the screen first loads. What are you basing your left and right edge positions on? Are they stored in advance? Based on the screen dimensions and density? If it's based on the screen dimensions, you shouldn't be having this problem. Measure the screen width as follows (may vary dependent on Android versions, but there's lots of online tutorials for this):
Display d = getWindowManager().getDefaultDisplay();
Point point = new Point();
d.getSize(point);
int width = point.x;
int height = point.y;
Have those values before calculating anything. Adjust the initial coordinate locations based on the screen edges.
Does the box start with the same coordinates every time? And is there a screen in the app prior to the ones you're running above? If so for both, either:
What part of the Activity / Fragment lifecycle are you running the method in? If you're running it in onResume(), try running aspects of it earlier in the cycle, or making special calculations early. More specifically:
Would you be able to provide the full Activity or Fragment that launches it and the rest of the method?
Note that I only too a brief look at the math itself and did't find any issues, so I'm going to assume it's fine since you said has no issues after loading completes.
Upvotes: 0
Reputation: 37645
This is very easy if you don't use equations of lines or slopes. What you have done is calculated all the x-coordinates by finding averages of pairs of x-coordinates. You've then substituted these x-coordinates into the equations of the sides. As you have identified this won't work if the slopes are infinite. There is a very simple solution to this. All you have to do is calculate y-coordinates in exactly the same way you calculated x-coordinates and the problem disappears.
Upvotes: 1