Is it possible to display custom text centered between 2 points on the graph?
I've got MPAndroidChart setup to display a step function type graph (representing hours spent doing a specific task) with horizontal and vertical lines only. What I would like to be able to do is show a label over the horizontal sections indicating the size of the section (aka the time spent calculated by taking the difference between the x values). Is there a way to do this? I've been look into modifying the library but I can't seem to figure out where would be the correct place to do so.
My best guess would be some changes in BarLineChartBase onDraw() method or maybe in the LineChartRenderer drawLinear() method.
Here is what I am able to produce:
Here is an example of what I am trying to produce:
Figured it out! Just add a new method drawTime() to the LineChart class at the end of onDraw() right after drawDescription(). Since each horizontal line is described by 2 Entry points I simply loop through 2 entries at a time for my single data set and calculate the difference:
protected void drawTime(Canvas c)
{
Paint timePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
timePaint.setTextSize(Utils.convertDpToPixel(16));
timePaint.setColor(Color.BLUE);
timePaint.setTextAlign(Paint.Align.CENTER);
MPPointD position;
LineData data = this.getLineData();
ILineDataSet dataSet = data.getDataSetByIndex(0);
for (int i = 1; i < dataSet.getEntryCount(); i+=2)
{
Entry e1 = dataSet.getEntryForIndex(i-1);
Entry e2 = dataSet.getEntryForIndex(i);
float time = e2.getX() - e1.getX();
position = getPixelForValues(e1.getX() + time/2, e1.getY() - 0.05f, YAxis.AxisDependency.LEFT);
c.drawText(String.valueOf(time), (float)position.x, (float)position.y, timePaint);
}
}
The resulting graph looks like this
Related
I am writing a program that outputs the shortest route between two points on a map. The problem is that if the route is too long and it has many points that define its path it slows the program a lot and I am looking for a way to draw just some points instead of all the points in the array.
My approach goes as follows: the map has a zoom, each time the zoom changes check which points overlaps with the others. All the points that doesn't overlap go into the routeToDraw list and then it is drawn.
To check if the points overlap or not I have the following function:
//route is a list of latitude and longitude points
LinkedList<Point.Double> route = MapPanel.this.getGlassPane().getRoute();
LinkedList<Point.Double> routeToDraw = new LinkedList<Point.Double>();
int ovalSize = 8;
boolean compareMorePoints;
for(int i = 0; i < route.size(); i++) {
Point p1 = getScreenCoordinates(route.get(i).x, route.get(i).y);
compareMorePoints = true;
int j = i + 1;
while (j < route.size() && compareMorePoints == true) {
Point p2 = getScreenCoordinates(route.get(j).x, route.get(j).y);
if (Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) > ovalSize ) {
routeToDraw.add(route.get(i));
compareMorePoints = false;
}
j++;
}
}
MapPanel.this.getGlassPane().setRouteToDraw(routeToDraw);
The problem is that this function is quite expensive and although it does reduce the amount of points to draw and I seem to obtain some speed after calculating routeToDraw I don't think it is worth the wait each time I zoom in or out.
The ideal solution would be something like Google Maps' does when routing, drawing a series of equidistant points that modify each time you zoom in or out and look quite nice.
Two suggestions...
(old trick)... Don't do unnecessary math inside of a loop. You can and should eliminate the sqrt function, which is an "expensive" math operation when doing distances. Just compare to the square of ovalSize. It is mathematically equivalent.
Is your list sorted in any way? If there were a convenient point in your program to sort your list (or a copy of it) before displaying, then you could very quickly:
Lop off the first and last part that is outside your zoom window in one of the coordinates (say X, if you sorted by X) by doing a binary search for the window boundary
Tighten up your loop to only look at neighbors within a window of concern, and do a sliding window instead of all-compared-to-all.
My question does not refer to what operators I need to use to manipulate matrices, but rather what is actually being sought by doing this procedure.
I have, for example, an image in matrix form on which I need to perform several operations (this filter is one of them). After converting said image to grayscale, I need to apply the following filter
float[][] smoothKernel = {
{0.1f,0.1f,0.1f},
{0.1f,0.2f,0.1f},
{0.1f,0.1f,0.1f}
};
on it.
The assignment file gives this example , so I assumed that when asked to "smooth" the image, I had to replace every individual pixel with an average of its neighbors (while also making sure special cases such as corners or side were handled properly).
The basic idea is this:
public static float[][] filter(float[][] gray, float[][] kernel) {
// gray is the image matrix, and kernel is the array I specifed above
float current = 0.0f;
float around = 0.0f;
float[][] smooth = new float[gray.length][gray[0].length];
for (int col = 0; col < gray.length; col++) {
for (int row = 0; row < gray[0].length; row++) {
//first two for loops are used to do this procedure on every single pixel
//the next two call upon the respective pixels around the one in question
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
around = at(gray, i + col, j + row); //This calls a method which checks for the
//pixels around the one being modified
current += around * kernel[i+1][j+1];
//after the application of the filter these are then added to the new value
}
}
smooth[col][row] = current;
current = 0.0f;
//The new value is now set into the smooth matrix
}
}
return smooth;
}
My dilemma lies in if I have to create this new array float[][] smooth; so as to avoid overriding the values of the original (the image outputted is all white in this case...). From the end product in the example I linked above I just cannot understand what is going on.
What is the correct way of applying the filter? Is this a universal method or does it vary for different filters?
Thank you for taking the time to clarify this.
EDIT: I have found the two errors which I detailed in the comments below, implemented back into the code, everything is working fine now.
I have also been able to verify that some of the values in the example are calculated incorrectly (thus contributing to my confusion), so I will be sure to point it out in my next class.
Question has been solved by ulterior methods, I am however not deleting it in hopes other people can benefit from it. The original code can be found in the edits.
A more advanced colleague of mine helped me to note that I was missing two things: one was the issue with resetting the current variable after computing the "smoothed" variables in the new array (resulting in a white image because this value would get increasingly larger thus surpassing the binary color limit, so it was set to the max). The second issue was that I was continuously iterating on the same pixel, which caused the whole image to have the same color (I was iterating the new array). So I added these specifications in, and all works fine since.
I have an app that takes in input from the user and then on the clicking of a button displays the calculated results in a format like so:
123456
213456
214356
124365
I need a line ( preferably blue) to join each of the number 2's in the list as they make their way down the TextView.
I also need the option of not having this line if the user does not want it.
What I have tried so far:
I extended a TextView class and overrode the onDraw(Canvas) method and tried to get something working and managed to display vertical blue lines but couldnt get the joining of the number 2's. I am unsure how android decides when onDraw() is called, as I dont call it in my code but I would rather I could so I could control when it displays the lines or not.
This is only idea:
What you have:
Given the text size lets say: 20px
X and Y coordinates of the view.
Gravity supplied to the content by the TextView if any
Text size in px. getTextSize() or paint.measureText(); [rect.right on Canvas is the text size]
Text ems. getEms()
Optional: Padding or margin if any
What you need to find:
Each x-coordinate of each letter on the canvas.
Each y-coordinate of each letter on the canvas.
How to:
x = letter position in the text * font size(or ems). if there is padding or margin add padding left + padding right to the x; [rect.left on Canvas is x]
Reset letter position each new line
y = line number * font size (or ems). + padding top + padding bottom [rect.bottom on Canvas is y]
Collect all approximate x and y's to form a Point.
Match each Point in onDraw()
If you think you want to use paint.measureText("13332", 0, "13332".indexOf("2")), measured width will be approximate and absolute to itself x-coordinate of "2". Relative to its parent might be much more complex to find.
Edit:
What I wrote above can be easily obtained using paint.[getTextBounds()][1] method which will give you Rect from which you can form a Point
I would proceed this way.
The user enters the value and clicks the button
As the button is tapped hide the TextView and replace it with an ImageView or keep the TextView and put the ImageView on the top of the TextView (Overlap of the ImageView on the TextView can be achieved with a FrameLayout
Draw in the ImageView the lines and the numbers in the OnDraw() method
As the user taps on the ImageView, hide the ImageView and display the TextView again.
Finally if the position of the 2's is fixed you don't need to measure the text.
If the posision is not fixed you can put the numbers in an array. Here is some code in mixed order (not tested)
var resStr1 = Integer.toString(resultNumber);
var position = 0;
...
// METHOD 1, multiple 2's in a number
var j = 0;
for (int i = 0; i < resStr1.length(); i++){
char c = s.charAt(i);
if (c == '2') {
position[j] = i;
j++;
}
}
// METHOD 2. ONLY ONE 2 in a number
position = resStr1.indexOf('2');
// CONTINUE
...
Paint p = new Paint();
...
float left = customMarginLeft + p.measureText(resStr1.substring(0, position));
float top = customMarginTop;
...
canvas.drawRect( bla bla bla...
drawText() // ??
Get a look also to getTextBounds() if you wanna to get the text height too.
About the onDraw method, don't take care of how much time it will be called by the system, if performance is critical just mantain the scope of the variables containing the results and the precalculations global, put in the main class the properties and methods that controls the behaviour of the drawing method. The system will redraw every time the lines again and again as soon as another element overlaps your control or something happens in the system so the fact that the onDraw is called again and again is normal, otherwise your lines will not be redrawn again and could disappear from the screen if something happens.
Of course the code above can be also put in a custom control (combined control).
To clear the lines you should call the invalidate() or postInvalidate() method. This methods will clear the whole area and force the onDraw() to be called again. Then put globally a flag like
shouldRedrawLines = false;
and in the onDraw() do something like this:
if (shouldRedrawLines) { // please note that the onDraw is called again and again and this condition allows you to check if in another part of the program you decided to clear the lines
DrawLines(); // contains the code for redrawing lines
}
DrawNumbersFromResult(); // contains the code for redrawing Numbers
Simple not ?
I have a line segment that represents a direction and magnitude (length), when i draw the segment it works as it should. the value getAimArmsRotation is being pulled from another class that contains a touchpad value.
if (player.getFacingRight()) {
lOriginX = (player.getPosition().x + Player.SIZEw/2);
lOriginY = (player.getPosition().y + Player.SIZEh/1.5f);
//lEndX = lOriginX + (float)Math.cos((player.getAimArmsRotation())/57) * 15f;
//lEndY = lOriginY + (float)Math.sin((player.getAimArmsRotation())/57) * 15f;
laserO = new Vector2(lOriginX, lOriginY);
laserE = new Vector2(lEndX, lEndY);
However if I use the Vectors or floats from this calculation and apply them to a model's velocity vector it does not move the model along the line segment as I would think it should.
EDIT: Sorry meant to attach this picture when I created the question. Fig 1 is how my line segment looks, when I set the velocity values that make up the line segment to my object it moves in the direction that fig 2 shows.
getAimArmsRotation() is just a method that sets a sprite's rotation with a value from the touchpad in another class. I don't think that the values should matter since these floats are what i've used in order to give the line segment it's length and direction, I would think that giving an object a velocity of the x and y floats would give it the same direction as the line?
Thanks for the DV, jerks.
I wasn't taking into account the origin position of the object when trying to send it along the desired path. I was only using the LineEnd values, I needed to give the object it's origin point to correctly calculate the trajectory or path.
for (GameObject go: gObjects) {
if (go.getType() == PROJECTILE_ID) {
go.getVelocity().x = player.getLineEndX() - player.getLineOrgX();
go.getVelocity().y = player.getLineEndY() - player.getLineOrgY();
System.out.println(go.getVelocity().x);
System.out.println(go.getVelocity().y);
}
}
I have two GPS locations. For each I am creating a bounding box in a different range.
Each bounding box has min/max latitude and min/max longitude.
Need to implement a method to detect if those two boxes overlap (don't mind the overlap range.. only true/false). Also, this method will be integrated in a long loop so I am looking for the most efficient way to do it.
note: when saying overlap I mean - "there is at least one single point on the map that is contained in both bounding boxes".
Any ideas?
I'm facing the same issue and the previous solution is not sufficient.
This image show cases that are covered and not covered
I've found this web page that give the correct method to respond to the problem: https://rbrundritt.wordpress.com/2009/10/03/determining-if-two-bounding-boxes-overlap/
Here is the implementation of this solution:
function DoBoundingBoxesIntersect(bb1, bb2) {
//First bounding box, top left corner, bottom right corner
var ATLx = bb1.TopLeftLatLong.Longitude;
var ATLy = bb1.TopLeftLatLong.Latitude;
var ABRx = bb1.BottomRightLatLong.Longitude;
var ABRy = bb1.BottomRightLatLong.Latitude;
//Second bounding box, top left corner, bottom right corner
var BTLx = bb2.TopLeftLatLong.Longitude;
var BTLy = bb2.TopLeftLatLong.Latitude;
var BBRx = bb2.BottomRightLatLong.Longitude;
var BBRy = bb2.BottomRightLatLong.Latitude;
var rabx = Math.abs(ATLx + ABRx – BTLx – BBRx);
var raby = Math.abs(ATLy + ABRy – BTLy – BBRy);
//rAx + rBx
var raxPrbx = ABRx – ATLx + BBRx – BTLx;
//rAy + rBy
var rayPrby = ATLy – ABRy + BTLy – BBRy;
if(rabx <= raxPrbx && raby <= rayPrby)
{
return true;
}
return false;
}
We can adapt the solution like that :
step 1 : check if the 2 boundingbox overlap on longitude
the left longitude of a bondingbox1 is between longMin and longMax of the boundingbox2 OR the right longitude of a bondingbox1 is between longMin and longMax of the boundingbox2
step 2 : check if the 2 boundingbox overlap on latitude
the top latitude of a bondingbox1 is between latMin and latMax of the boundingbox2 OR the bottom longitude of a bondingbox1 is between latMin and latMax of the boundingbox2
if step1 and step 2 are right, then the 2 boundingbox overlap
You can see the corresponding sketch here :
It's enough to check if one of the corners of one rectangle is within the other rectangle. This is true of these two hold:
rect1.minX or rect1.maxX is between rect2.minX and rect2.maxX
and
rect1.minY or rect1.maxY is between rect2.minY and rect2.maxY
This check should take no time at all to do, so efficiency isn't a problem. Also, the order of the arguments is irrelevant.