I am developing a small chart app to monitor the blood sugar with the MPAndroidChart library.
Is there a way to draw the text inside the circle without offset?
As suggested in another post, i put in a slight y-offset (different y-data for circle and text) and achieved a result which is ok.
But if there are only two values the text and circle do not align.
I am not that familiar with modifying the yAxis Renderer, do you know how to find the formula behind the offset for the labels? (would be great to just recalculate it..)
Similar problem: How to place the text values inside in MPAndroidChart circle?
With manual offset between text and circle y-data
Due to zooming in on y-axis (less data points and range on y-axis) the manuel offset is not working well here..
Otherwise awesome library PhilJay !!
EDIT: I have found a easy and sufficient solution:
Override the drawValues method from LineChartRenderer**
In LineChartRenderer.java -> drawValues the text is vertically shifted by this line:
drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));
So to get rid of the "- valOffset":
1.Override the drawValues method
Create a new java file "CenteredTextLineChartRenderer.java" and override method drawValues from LineChartRenderer
2.Modify the y-valOffset to y+textHeight*0.35f
Add float textHeight = dataSet.getValueTextSize();
public class CenteredTextLineChartRenderer extends LineChartRenderer {
public CenteredTextLineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
//Modified drawValues Method
// Center label on coordinate instead of applying a valOffset
#Override
public void drawValues(Canvas c) {
if (isDrawingValuesAllowed(mChart)) {
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
for (int i = 0; i < dataSets.size(); i++) {
ILineDataSet dataSet = dataSets.get(i);
float textHeight = dataSet.getValueTextSize();
if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1)
continue;
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
// make sure the values do not interfear with the circles
int valOffset = (int) (dataSet.getCircleRadius() * 1.75f);
if (!dataSet.isDrawCirclesEnabled())
valOffset = valOffset / 2;
mXBounds.set(mChart, dataSet);
float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator
.getPhaseY(), mXBounds.min, mXBounds.max);
ValueFormatter formatter = dataSet.getValueFormatter();
MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y);
for (int j = 0; j < positions.length; j += 2) {
float x = positions[j];
float y = positions[j + 1];
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))
continue;
Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min);
if (dataSet.isDrawValuesEnabled()) {
//drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));
drawValue(c, formatter.getPointLabel(entry), x, y+textHeight*0.35f, dataSet.getValueTextColor(j / 2));
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
Utils.drawImage(
c,
icon,
(int)(x + iconsOffset.x),
(int)(y + iconsOffset.y),
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
MPPointF.recycleInstance(iconsOffset);
}
}
}
}
3.Set your own LineChart renderer to your modified drawValues class
LineChart mChart = (LineChart) mainActivity.findViewById(R.id.LineChart);
mChart.setRenderer(new CenteredTextLineChartRenderer(mChart,mChart.getAnimator(),mChart.getViewPortHandler()));
Run your code and manually adapt the 0.35f offset in your CenteredTextLineChartRenderer class
Now your text is always vertically centered!
IMPORTANT: With deleting the valOffset your label is not vertically centered as the text anchor is not in the center of your text label. So you have to insert a manual offset "textHeight*0.35f" (just try it out).
I'm currently recreating a Civilization game in Processing. I'm planning to implement the feature in a which a given unit can see every possible move it can make with a given number of hexes it is allowed to move. All possible endpoints are marked with red circles. However, units cannot move through mountains or bodies of water. I'm trying to approach this by finding out every possible combination of moves I can make without the unit going into a mountain or body of water but I can't figure out how I can determine every combination.
There are 6 directions that any unit can go in, north-east, north, north-west, south-east, south, south-west. The max number of movements I'm assigning to any unit would probably go up to 6. Any higher and I'm afraid processing may become to slow every time I move a unit.
I'm trying to recreate this:
What I'm hoping the result will look like with two possible movements (without the black arrows):
Raw version of that image:
Here is the code I use to draw the hex grid. Immediately after drawing each individual hex, its center's x coords and y coords are stored in xHexes and yHexes respectively. Also, immediately after generating the type of tile (e.g. grass, beach), the type of tile is also stored in an array named hexTypes. Therefore, I can get the x and y coords and type of hex of any hex I want on the map just by referencing its index.
Code used to draw a single hexagon:
beginShape();
for (float a = PI/6; a < TWO_PI; a += TWO_PI/6) {
float vx = x + cos(a) * gs*2;
float vy = y + sin(a) * gs*2;
vertex(vx, vy);
}
x is the x coord for centre of hexagon
y is the y coord for centre of hexagon
gs = radius of hexagon
Code used to tesselate hex over the window creating a hex grid:
void redrawMap() {
float xChange = 1.7;
float yChange = 6;
for (int y = 0; y < ySize/hexSize; y++) {
for (int x = 0; x < xSize/hexSize; x++) {
if (x % 2 == 1) {
// if any part of this hexagon being formed will be visible on the window and not off the window.
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange, hexSize);
}
// only record and allow player to react with it if the entire tile is visible on the window
if (x*hexSize*xChange < width && int(y*hexSize*yChange) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange));
}
} else {
if (x*hexSize*xChange <= width+2*hexSize && int(y*hexSize*yChange) <= height+3*hexSize) {
drawHex(x*hexSize*xChange, y*hexSize*yChange+(hexSize*3), hexSize);
}
if (x*hexSize*xChange < width && int(y*hexSize*yChange+(hexSize*3)) < height) {
xHexes.add(int(x*hexSize*xChange));
yHexes.add(int(y*hexSize*yChange+(hexSize*3)));
}
}
}
}
}
hexSize is a user-specified size for each hexagon, determining the number of hexagons that will be on the screen.
This answer will help you get to this (green is plains, red is hills and blue is water, also please don't flame my terrible grid):
Note that there is no pathfinding in this solution, only some very simple "can I get there" math. I'll include the full code of the sketch at the end so you can reproduce what I did and test it yourself. One last thing: this answer doesn't use any advanced design pattern, but it assume that you're confortable with the basics and Object Oriented Programming. If I did something which you're not sure you understand, you can (and should) ask about it.
Also: this is a proof of concept, not a "copy and paste me" solution. I don't have your code, so it cannot be that second thing anyway, but as your question can be solved in a bazillion manners, this is only one which I deliberately made as simple and visual as possible so you can get the idea and run with it.
First, I strongly suggest that you make your tiles into objects. First because they need to carry a lot of information (what's on each tile, how hard they are to cross, maybe things like resources or yield... I don't know, but there will be a lot of stuff).
The Basics
I organized my global variables like this:
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>(); // all the tiles
ArrayList<Tile> _canTravel = new ArrayList<Tile>(); // tiles you can currently travel to
The basics being that I like to be able to change my grid size on the fly, but that's just a detail. What's next is to choose a coordinate system for the grid. I choose the simplest one as I didn't want to bust my brain on something complicated, but you may want to adapt this to another coordinate system. I choose the offset coordinate type of grid: my "every second row" is half a tile offset. So, instead of having this:
I have this:
The rest is just adjusting the spatial coordinates of the tiles so it doesn't look too bad, but their coordinates stays the same:
Notice how I consider that the spatial coordinates and the grid coordinates are two different things. I'll mostly use the spatial coordinates for the proximity checks, but that's because I'm lazy, because you could make a nice algorithm which do the same thing without the spatial coordinates and it would probably be less costly.
What about the travel points? Here's how I decided to work: your unit has a finite amount of "travel points". Here there's no unit, but instead a global variable unitTravelPoints which will do the same thing. I decided to work with this scale: one normal tile is worth 10 travel points. So:
Plains: 10 points
Hills: 15 points
Water: 1000 points (this is impassable terrain but without going into the details)
I'm not going to go into the details of drawing a grid, but that's mostly because your algorithm looks way better than mine on this front. On the other hand, I'll spend some time on explaining how I designed the Tiles.
The Tiles
We're entering OOP: they are Drawable. Drawable is a base class which contains some basic info which every visible thing should have: a position, and an isVisible setting which can be turned off. And a method to draw it, which I call Render() since draw() is already taken by Processing:
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
The Tile will be more sophisticated. It'll have more basic informations: row, column, is it currently selected (why not), a type like plains or hills or water, a bunch of neighboring tiles, a method to draw itself and a method to know if the unit can travel through it:
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
The Tile Types
The TileType is a strange animal: it's a real class, but it's never used anywhere. That's because it's a common root for all tile types, which will inherit it's basics. The "City" tile may need very different variables than, say, the "Desert" tile. But both need to be able to draw themselves, and both need to be owned by the tiles.
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
Each tile type can be custom, now, yet each tile is... just a tile, whatever it's content. Here are the TileType I used in this demonstration:
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
Non-class methods
I added a mouseClicked() method so we can select a hex to check how far from it the unit can travel. In your game, you would have to make it so when you select a unit all these things fall into place, but as this is just a proof of concept the unit is imaginary and it's location is wherever you click.
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
At last, I added 2 "helper methods" to make things easier:
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
How Travel is calculated
Behind the scenes, that's how the program knows where the unit can travel: in this example, the unit has 30 travel points. Plains cost 10, hills cost 15. If the unit has enough points left, the tile is marked as "can travel there". Every time a tile is in travel distance, we also check if the unit can get further from this tile, too.
Now, if you're still following me, you may ask: how do the tiles know which other tile is their neighbor? That's a great question. I suppose that an algorithm checking their coordinates would be the best way to handle this, but as this operation will happen only once when we create the map I decided to take the easy route and check which tiles were the close enough spatially:
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
Complete code for copy-pasting
Here's the whole thing in one place so you can easily copy and paste it into a Processing IDE and play around with it (also, it includes how I draw my awful grid):
// Debug
int unitTravelPoints = 30; // this is the number if "travel points" currently being tested, you can change it
// Golbals
float _tileSize = 60;
int _gridWidth = 10;
int _gridHeight = 20;
ArrayList<Tile> _tiles = new ArrayList<Tile>();
ArrayList<Tile> _canTravel = new ArrayList<Tile>();
void settings() {
// this is how to make a window size's dynamic
size((int)(((_gridWidth+1) * 1.5) * _tileSize), (int)(((_gridHeight+1) * 0.5) * _tileSize));
}
void setup() {
// create the grid
for (int i=0; i<_gridWidth; i++) {
for (int j=0; j<_gridHeight; j++) {
int rand = (int)random(100);
if (rand < 20) {
_tiles.add(new Tile(j, i, new Water()));
} else if (rand < 50) {
_tiles.add(new Tile(j, i, new Hill()));
} else {
_tiles.add(new Tile(j, i, new Plains()));
}
}
}
// detect and save neighbor tiles for every Tile
for (Tile currentTile : _tiles) {
for (Tile t : _tiles) {
if (t != currentTile) {
if (IsPointInRadius(currentTile.position, t.position, _tileSize)) {
currentTile.neighbors.add(t);
}
}
}
}
}
void draw() {
background(0);
// show the tiles
for (Tile t : _tiles) {
t.Render();
}
// show how far you can go
for (Tile t : _canTravel) {
fill(0, 0, 0, 0);
if (t.selected) {
stroke(255);
} else {
stroke(0, 255, 0);
}
strokeWeight(5);
DrawPolygon(t.position.x, t.position.y, _tileSize/2, 6);
}
}
class Drawable {
PVector position;
boolean isVisible;
public Drawable() {
position = new PVector(0, 0);
isVisible = true;
}
public void Render() {
// If you forget to overshadow the Render() method you'll see this error message in your console
println("Error: A Drawable just defaulted to the catch-all Render(): '" + this.getClass() + "'.");
}
}
class Tile extends Drawable {
int row, column;
boolean selected = false;
TileType type;
ArrayList<Tile> neighbors = new ArrayList<Tile>();
Tile(int row, int column, TileType type) {
super(); // this calls the parent class' constructor
this.row = row;
this.column = column;
this.type = type;
// the hardcoded numbers are all cosmetics I included to make my grid looks less awful, nothing to see here
position.x = (_tileSize * 1.5) * (column + 1);
position.y = (_tileSize * 0.5) * (row + 1);
// this part checks if this is an offset row to adjust the spatial coordinates
if (row % 2 != 0) {
position.x += _tileSize * 0.75;
}
}
// this method looks recursive, but isn't. It doesn't call itself, but it calls it's twin from neighbors tiles
void FillCanTravelArrayList(int travelPoints, boolean originalTile) {
if (travelPoints >= type.travelCost) {
// if the unit has enough travel points, we add the tile to the "the unit can get there" list
if (!_canTravel.contains(this)) {
// well, only if it's not already in the list
_canTravel.add(this);
}
// then we check if the unit can go further
for (Tile t : neighbors) {
if (originalTile) {
t.FillCanTravelArrayList(travelPoints, false);
} else {
t.FillCanTravelArrayList(travelPoints - type.travelCost, false);
}
}
}
}
void Render() {
if (isVisible) {
// the type knows which colors to use, so we're letting the type draw the tile
type.Render(this);
}
}
}
class TileType {
// cosmetics
color fill = color(255, 255, 255);
color stroke = color(0);
float strokeWeight = 2;
// every tile has a "travelCost" variable, how much it cost to travel through it
int travelCost = 10;
// while I put this method here, it could have been contained in many other places
// I just though that it made sense here
void Render(Tile tile) {
fill(fill);
if (tile.selected) {
stroke(255);
} else {
stroke(stroke);
}
strokeWeight(strokeWeight);
DrawPolygon(tile.position.x, tile.position.y, _tileSize/2, 6);
textAlign(CENTER, CENTER);
fill(255);
text(tile.column + ", " + tile.row, tile.position.x, tile.position.y);
}
}
// each different tile type will adjust details like it's travel cost or fill color
class Plains extends TileType {
Plains() {
this.fill = color(0, 125, 0);
this.travelCost = 10;
}
}
class Water extends TileType {
// here I'm adding a random variable just to show that you can custom those types with whatever you need
int numberOfFishes = 10;
Water() {
this.fill = color(0, 0, 125);
this.travelCost = 1000;
}
}
class Hill extends TileType {
Hill() {
this.fill = color(125, 50, 50);
this.travelCost = 15;
}
}
void mouseClicked() {
// clearing the array which contains tiles where the unit can travel as we're changing those
_canTravel.clear();
for (Tile t : _tiles) {
// select the tile we're clicking on (and nothing else)
t.selected = IsPointInRadius(t.position, new PVector(mouseX, mouseY), _tileSize/2);
if (t.selected) {
// if a tile is selected, check how far the imaginary unit can travel
t.FillCanTravelArrayList(unitTravelPoints, true);
}
}
}
// checks if a point is inside a circle's radius
boolean IsPointInRadius(PVector center, PVector point, float radius) {
// simple math, but with a twist: I'm not using the square root because it's costly
// we don't need to know the distance between the center and the point, so there's nothing lost here
return pow(center.x - point.x, 2) + pow(center.y - point.y, 2) <= pow(radius, 2);
}
// draw a polygon (I'm using it to draw hexagons, but any regular shape could be drawn)
void DrawPolygon(float x, float y, float radius, int npoints) {
float angle = TWO_PI / npoints;
beginShape();
for (float a = 0; a < TWO_PI; a += angle) {
float sx = x + cos(a) * radius;
float sy = y + sin(a) * radius;
vertex(sx, sy);
}
endShape(CLOSE);
}
Hope it'll help. Have fun!
You will have to use similar algorithms we use on pathfinding. you create a stack or queue that will hold a class storing the position of the hex and the number of moves left from that point, initially you insert your starting position with the number of moves you have and mark that hex as done ( to not re-use a position you have already been on ), then you pop an element, and you insert every neighbor of that hex with a number of moves -1. when you insert the hexes with zero moves, those are your endpoints. And before inserting any hex check if it's not already done.
I hope I was clear, your question was a bit vague but I tried to give you an idea of how these solutions are usually done, also I think your question fits more into algorithms rather then processing
Best of luck
I am trying to write a small program that has a given number of balls (in the example code below it's 3) travel back and forth across the screen at different speeds and phases (start offset).
This much has been achieved in the code. Although I want to be able to select the balls (one at a time) using a mouse click.
I have used the word "HIT!!!" to signify in the console that a ball has been clicked.
My problem is that when I run the code below, I only get a "HIT!" in the console when I click the top ball. That is when the first element y[0] matches with the click_Y variable. When I am sure (but obviously mistaken somehow) that there should be matches when I click in the vicinity of y[1] & y[2].
I'd really be grateful for any help with these. As it's gotten to the point where I am starting to stare blankly at the screen. Thanks.
int noCircles; // the number of items in the array (# of circles)
float[] y; // y-position of each circle (fixed)
float[] speed; // speed of each circle
float[] phase; // phase of each circle
float red = 120;
float green = 120;
float blue = 120;
float click_X;
float click_Y;
void setup() {
size(500, 500);
noCircles = 3;
// allocate space for each array
y = new float[noCircles];
speed = new float[noCircles];
phase = new float[noCircles];
// calculate the vertical gap between each circle based on the total number
// of circles
float gap = height / (noCircles + 1);
//setup an initial value for each item in the array
for (int i=0; i<noCircles; i++) {
y[i] = gap * (i + 1);
// y is constant for each so can be calculated once
speed[i] = random(10);
phase[i] = random(TWO_PI);
}
}
void draw() {
background(155);
for (int i=0; i<noCircles; i++) {
// calculate the x-position of each ball based on the speed, phase and
//current frame
float x = width/2 + sin(radians(frameCount*speed[i] ) + phase[i])* 200;
if (dist(x, y[i], click_X, click_Y) <= 20){
println("HIT!!!!!!!!!!!!!!!!!!");
}
ellipse(x, y[i], 20, 20);
click_X = 0;
click_Y = 0;
}
}
void mousePressed() {
println("You clicked******************************************");
click_X = mouseX;
click_Y = mouseY;
println("click_X =" + click_X);
println("click_Y =" + click_Y);
}
Problems like these are best solved by debugging your program. Start by tracing through the code by hand, then add print statements (more than you've already added), and if that doesn't work then don't be afraid to use the debugger.
You're using the click_X and click_Y variables to check the position of the mouse against the position of each ball. Trace through the for loop in your draw() function. What happens at the end of the first iteration?
You reset the values of click_X and click_Y. That's why you aren't detecting any hits on the other circles.
You could probably refactor your code to only reset those variables if something has been hit, but really, I would stop using them altogether.
I'm guessing that you're using those variables because you only want to check when the mouse is pressed? Just use the mousePressed variable for that. Then you can use the mouseX and mouseY variables directly.
Then your if statement would look like this:
if (mousePressed && dist(x, y[i], mouseX, mouseY) <= 20) {
println("HIT: " + i);
}
Also, using separate arrays like this is called parallel arrays, and is general a bad habit to get into. You should probably use classes instead.
I have a program that works well; however, I want to be able to copy an earth image to a different section of the new image that is created. For example, if I could place the picture of the Earth on the bottom left corner instead of the top left corner.
import java.awt.*;
public class CopyCatDemo
{
public static void main(String[] args)
{
Picture sourcePicture = new Picture("earth.jpg");
System.out.println("Width: " + sourcePicture.getWidth());
System.out.println("Height: " + sourcePicture.getHeight());
Picture targetPicture1 = new Picture(800,800);
targetPicture1.setAllPixelsToAColor(Color.BLACK);
Pixel sourcePixel, targetPixel = null;
Color sourceColor, targetColor = null;
for(int y = 0; y < sourcePicture.getHeight(); y++)
{
for(int x = 0; x < sourcePicture.getWidth(); x++)
{
sourcePixel = sourcePicture.getPixel(x,y);
sourceColor = sourcePixel.getColor();
targetPixel = targetPicture1.getPixel(x,y);
targetPixel.setColor(sourceColor);
}
}
sourcePicture.show();
targetPicture1.show();
targetPicture1.write("NewFile.jpg");
}//end of main method
}//end of class
So, if someone could please demonstrate how to edit this code to get the picture of the Earth to appear on the bottom left corner as an example on the new target image that would be appreciated! Thanks!
if I could place the picture of the Earth on the bottom left corner instead of the top left corner.
Do the proper math to offset the coordinates - for example to move to the bottom left you need to vertically offset the target pixel - in other words offset the y value for the target pixel by the height of the target minus the height of the source
int vOffset = targetPicture1.getHeight() - sourcePicture.getHeight();
//
targetPixel = targetPicture1.getPixel(x, vOffset + y);
I found this question that deals with the same issue. The provided answers work, but I need to change it slightly for my case. Below is the answer I went with:
double theta = Math.atan2(pointerY - height / 2, pointerX - width / 2);
if(theta<0)
theta = Math.PI - theta;
int whichSlice = 0;
double sliceSize = Math.PI*2 / 4;
double sliceStart;
for(int i=1; i<=4; i++) {
sliceStart = i*sliceSize;
if(theta < sliceStart) {
whichSlice = i;
break;
}
}
In my case, I need to rotate the quadrants by 45 degrees. Below is an example; red is what this code does, while green is what I want:
I've tried various code alterations, but still can't figure it out.
EDIT:
First off, create your circle in it's own desperate JComponent, and add it's own listeners - basically create a class for this circle, make the circle itself receive mouse events, and MAKE SURE THAT THE CIRCLE OCCUPIES THE ENTIRE RECTANGLE OF THE JCOMPONENT - it must be touching all edges (I will be using this.getHeight() and this must return the height of the bounding box of the circle)!!!
Fixed code below to support such a case, in addition to support y axis which increases downwards:
Step 1:
Check if we are inside the circle.
Step 2:
Check if we are above/below the diagonal lines (note: equations for diagonal lines are y = x, and y = -x)
Point pointWeAreChecking;
Point centerOfCircle;
double radius;
if(Math.pow(Math.pow(pointWeAreChecking.x-centerOfCircle.x , 2) + Math.pow(pointWeAreChecking.y-centerOfCircle.y , 2), 0.5) <= radius)
{
//Means we are in circle.
if(pointWeAreChecking.y>pointWeAreChecking.x)
{
//Means it is either in 2 or 3 (it is below y = -x line)
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight()){
//We are in 2.
}else
{
//We are in 3.
}
}else
{
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight())
{
//We are in 4.
}else
{
//We are in 2.
}
}
}