Alright, so I'm working on collision detection for a 3d game, this is what I got so far:
public void mapCol(Spatial map, Node model2){
Mesh m = (Mesh) ((Node) map).getChild("obj_mesh0");
int c = 0;
m.updateWorldBound(true);
boolean col = false;
c = m.getMeshData().getPrimitiveCount(0);
// System.out.println(c);
Vector3[][] v3 = new Vector3[c][3];
for(int s = 0; s < c; s++){
v3[s] = null;
v3[s] = m.getMeshData().getPrimitive(s, 0, v3[s]);
Vector3 min = new Vector3((float)Math.min((float) Math.min(v3[s][0].getXf(), v3[s][1].getXf()), v3[s][2].getXf()),
(float)Math.min((float)Math.min(v3[s][0].getYf(), v3[s][1].getYf()), v3[s][2].getYf()),
(float)Math.min((float)Math.min(v3[s][0].getZf(), v3[s][1].getZf()), v3[s][2].getZf()));
Vector3 max = new Vector3((float) Math.max((float)Math.max(v3[s][0].getXf(), v3[s][1].getXf()), v3[s][2].getXf()),
(float)Math.max((float)Math.max(v3[s][0].getYf(), v3[s][1].getYf()), v3[s][2].getYf()),
(float)Math.max((float)Math.max(v3[s][0].getZf(), v3[s][1].getZf()), v3[s][2].getZf()));
Vector3 v2 = new Vector3();
v2 = max.add(min, v2);
v2.divideLocal(2);
if(max.getXf() > model2.getTranslation().getXf() - sp1.getRadius()&&
min.getXf() < model2.getTranslation().getXf() + sp1.getRadius() &&
max.getZf() > model2.getTranslation().getZf() - sp1.getRadius() &&
min.getZf() < model2.getTranslation().getZf() + sp1.getRadius() &&
max.getYf() > model2.getTranslation().getYf() + sp1.getRadius()&&
!col){
float cosine = (float) v2.dot(v2);
float angle = (float) Math.toDegrees(Math.acos( cosine ));
float pangle = (float) Math.toDegrees(Math.atan2((min.getX() + ((max.getX() - min.getX())/2)) - model2.getTranslation().getX(), (min.getZ() + ((max.getZ() - min.getZ())/2) - model2.getTranslation().getZ())));
if(min.getY() < max.getY()){
System.out.println("pangle:" + pangle + " angle:" + angle);
model2.setTranslation(
(min.getX() + ((max.getX() - min.getX())/2)) - (Math.sin(Math.toRadians(pangle)) * (sp1.getRadius())),
model2.getTranslation().getYf(),
(min.getZ() + ((max.getZ() - min.getZ())/2)) - (-Math.cos(Math.toRadians(pangle)) * (sp1.getRadius()))
);
col = true;
}
}
}
}
Now the part to really look at is right here:
model2.setTranslation(
(min.getX() + ((max.getX() - min.getX())/2)) - (Math.sin(Math.toRadians(pangle)) * (sp1.getRadius())),
model2.getTranslation().getYf(),
(min.getZ() + ((max.getZ() - min.getZ())/2)) - (-Math.cos(Math.toRadians(pangle)) * (sp1.getRadius()))
);
Any idea why it wouldn't set model2 modle2's radius away from the wall? (making it stop at the way and able to go no further)
float cosine = v2.dot(v2)
is intentional ?
Because it just gives you length of v2, squared.
Probably that should be
float cosine = velocity.dot(normalVector)/(velocity.length()*normalVector.length())
, if you wanted cosine of angle between them, but I don't fully understand your code, so I don't know.
Related
I am trying to create Android App with chart with variable line stroke width like this one in the link.
I have tried to realize it with MPAndroidChart. Override drawCubicBezier method in LineChartRenderer class and to set dynamically setStrokeWidth on Paint. But nothing happens. Probably my knowledge is not enough to do it.
Can you help me how to achieve a different line thickness or if there is another chart library that has such functionality?
This is how I set my new Renderer
chart2.setRenderer(new myLineChartRenderer(chart2, chart2.getAnimator(), chart2.getViewPortHandler()));
This is the class
public class myLineChartRenderer extends LineChartRenderer {
public myLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
#Override
protected void drawCubicBezier(ILineDataSet dataSet) {
Path mPath = new Path();
ArrayList<Path> mPaths = new ArrayList<Path>();
mRenderPaint.setAntiAlias(true);
mRenderPaint.setDither(true);
mRenderPaint.setStyle(Paint.Style.STROKE);
mRenderPaint.setStrokeJoin(Paint.Join.ROUND);
mRenderPaint.setStrokeCap(Paint.Cap.ROUND);
//mRenderPaint.setStrokeWidth(STROKE_WIDTH);
float phaseY = mAnimator.getPhaseY();
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
float intensity = dataSet.getCubicIntensity();
cubicPath.reset();
if (mXBounds.range >= 1) {
float prevDx = 0f;
float prevDy = 0f;
float curDx = 0f;
float curDy = 0f;
// Take an extra point from the left, and an extra from the right.
// That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart.
// So in the starting `prev` and `cur`, go -2, -1
// And in the `lastIndex`, add +1
final int firstIndex = mXBounds.min + 1;
final int lastIndex = mXBounds.min + mXBounds.range;
Entry prevPrev;
Entry prev = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0));
Entry cur = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0));
Entry next = cur;
int nextIndex = -1;
if (cur == null) return;
// let the spline start
cubicPath.moveTo(cur.getX(), cur.getY() * phaseY);
mPath.moveTo(cur.getX(), cur.getY() * phaseY);
for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) {
prevPrev = prev;
prev = cur;
cur = nextIndex == j ? next : dataSet.getEntryForIndex(j);
nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j;
next = dataSet.getEntryForIndex(nextIndex);
prevDx = (cur.getX() - prevPrev.getX()) * intensity;
prevDy = (cur.getY() - prevPrev.getY()) * intensity;
curDx = (next.getX() - prev.getX()) * intensity;
curDy = (next.getY() - prev.getY()) * intensity;
cubicPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY,
cur.getX() - curDx,
(cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY);
mPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY,
cur.getX() - curDx,
(cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY);
mPaths.add(mPath);
// Log.i("Curve", (prev.getX() + prevDx)+" | "+ ((prev.getY() + prevDy) * phaseY)+" | "+ (cur.getX() - curDx)+" | "+ ((cur.getY() - curDy) * phaseY)+" | "+ cur.getX()+" | "+ (cur.getY() * phaseY));
}
}
// if filled is enabled, close the path
if (dataSet.isDrawFilledEnabled()) {
cubicFillPath.reset();
cubicFillPath.addPath(cubicPath);
//drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds);
}
mRenderPaint.setColor(dataSet.getColor());
mRenderPaint.setStyle(Paint.Style.STROKE);
trans.pathValueToPixel(cubicPath);
//mBitmapCanvas.drawPath(cubicPath, mRenderPaint);
for(int i=0; i<mPaths.size();i++)
{
mRenderPaint.setStrokeWidth(i+30);
mBitmapCanvas.drawPath(mPaths.get(i), mRenderPaint);
}
mRenderPaint.setPathEffect(null);
}
}
I tried to divide the path into separate parts and change the thickness of the line while drawing them, but the result is totally wrong.
I've created an input handler for NASA Worldwind that I'm trying to replicate Google Earth like zooming with.
I'm trying to make zoom towards the mouse cursor, instead of the center of the screen (like it does by default).
I've got it somewhat working -- except it doesn't zoom towards the lat/long under the cursor consistently, it seems to drift too far. What I want to happen is that the same lat/long is held under the cursor during the duration of the zoom. So, for instance, if you are hovering the cursor over a particular landmark (like a body of water), it will stay under the cursor as the wheel is scrolled.
The code I'm using is based heavily on this: https://forum.worldwindcentral.com/forum/world-wind-java-forums/development-help/11977-zoom-at-mouse-cursor?p=104793#post104793
Here is my Input Handler:
import java.awt.event.MouseWheelEvent;
import gov.nasa.worldwind.awt.AbstractViewInputHandler;
import gov.nasa.worldwind.awt.ViewInputAttributes;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.view.orbit.BasicOrbitView;
import gov.nasa.worldwind.view.orbit.OrbitViewInputHandler;
public class ZoomToCursorViewInputHandler extends OrbitViewInputHandler {
protected class ZoomActionHandler extends VertTransMouseWheelActionListener {
#Override
public boolean inputActionPerformed(AbstractViewInputHandler inputHandler, MouseWheelEvent mouseWheelEvent,
ViewInputAttributes.ActionAttributes viewAction) {
double zoomInput = mouseWheelEvent.getWheelRotation();
Position position = getView().computePositionFromScreenPoint(mousePoint.x, mousePoint.y);
// Zoom toward the cursor if we're zooming in. Move straight out when zooming
// out.
if (zoomInput < 0 && position != null)
return this.zoomToPosition(position, zoomInput, viewAction);
else
return super.inputActionPerformed(inputHandler, mouseWheelEvent, viewAction);
}
protected boolean zoomToPosition(Position position, double zoomInput,
ViewInputAttributes.ActionAttributes viewAction) {
double zoomChange = zoomInput * getScaleValueZoom(viewAction);
BasicOrbitView view = (BasicOrbitView) getView();
System.out.println("================================");
System.out.println("Center Position: \t\t"+view.getCenterPosition());
System.out.println("Mouse is on Position: \t\t"+position);
Vec4 centerVector = view.getCenterPoint();
Vec4 cursorVector = view.getGlobe().computePointFromLocation(position);
Vec4 delta = cursorVector.subtract3(centerVector);
delta = delta.multiply3(-zoomChange);
centerVector = centerVector.add3(delta);
Position newPosition = view.getGlobe().computePositionFromPoint(centerVector);
System.out.println("New Center Position is: \t"+newPosition);
setCenterPosition(view, uiAnimControl, newPosition, viewAction);
onVerticalTranslate(zoomChange, viewAction);
return true;
}
}
public ZoomToCursorViewInputHandler() {
ViewInputAttributes.ActionAttributes actionAttrs = this.getAttributes()
.getActionMap(ViewInputAttributes.DEVICE_MOUSE_WHEEL)
.getActionAttributes(ViewInputAttributes.VIEW_VERTICAL_TRANSLATE);
actionAttrs.setMouseActionListener(new ZoomActionHandler());
}
}
To enable, set this property in the worldwind.xml to point to this class:
<Property name="gov.nasa.worldwind.avkey.ViewInputHandlerClassName"
value="gov.nasa.worldwindx.examples.ZoomToCursorViewInputHandler"/>
After some thinking over this problem I believe there is no closed form analytical solution for it. You just have to take into account to many things: shape of the Earth, how the "eye" moves when you move the center. So the best trick I think you can do is to "follow" the main "zoom" animation and do small adjustments after each animation step. As animation steps are small, calculation errors should also be smaller and they should accumulate less because on next step you take into account all the previous errors. So my idea in the code is roughly following: create a FixZoomPositionAnimator class as
static class FixZoomPositionAnimator extends BasicAnimator
{
static final String VIEW_ANIM_KEY = "FixZoomPositionAnimator";
static final double EPS = 0.005;
private final java.awt.Point mouseControlPoint;
private final Position mouseGeoLocation;
private final Vec4 mouseGeoPoint;
private final BasicOrbitView orbitView;
private final Animator zoomAnimator;
private int lastDxSign = 0;
private int lastDySign = 0;
int stepNumber = 0;
int stepsNoAdjustments = 0;
FixZoomPositionAnimator(BasicOrbitView orbitView, Animator zoomAnimator, java.awt.Point mouseControlPoint, Position mouseGeoLocation)
{
this.orbitView = orbitView;
this.zoomAnimator = zoomAnimator;
this.mouseControlPoint = mouseControlPoint;
this.mouseGeoLocation = mouseGeoLocation;
mouseGeoPoint = orbitView.getGlobe().computePointFromLocation(mouseGeoLocation);
}
public Point getMouseControlPoint()
{
return mouseControlPoint;
}
public Position getMouseGeoLocation()
{
return mouseGeoLocation;
}
private static int sign(double d)
{
if (Math.abs(d) < EPS)
return 0;
else if (d > 0)
return 1;
else
return -1;
}
double calcAccelerationK(double dSign, double lastDSign)
{
// as we are following zoom trying to catch up - accelerate adjustment
// but slow down if we overshot the target last time
if (!zoomAnimator.hasNext())
return 1.0;
else if (dSign != lastDSign)
return 0.5;
else
{
// reduce acceleration over time
if (stepNumber < 10)
return 5;
else if (stepNumber < 20)
return 3;
else
return 2;
}
}
static boolean isBetween(double checkedValue, double target1, double target2)
{
return ((target1 < checkedValue) && (checkedValue < target2))
|| ((target1 > checkedValue) && (checkedValue > target2));
}
static boolean isValid(Position position)
{
return isBetween(position.longitude.degrees, -180, 180)
&& isBetween(position.latitude.degrees, -90, 90);
}
#Override
public void next()
{
// super.next(); // do not call super to avoid NullPointerException!
nextWithTilt(); // works OK on tilted Earth
// nextOld(); // IMHO better looking but stops working is user tilts the Earth
}
private void nextOld()
{
stepNumber++;
Vec4 curProjection = orbitView.project(mouseGeoPoint);
Rectangle viewport = orbitView.getViewport();
// for Y sign is inverted
double dX = (mouseControlPoint.x - curProjection.x);
double dY = (mouseControlPoint.y + curProjection.y - viewport.getHeight());
if (Math.abs(dX) > EPS || Math.abs(dY) > EPS)
{
double dCX = (mouseControlPoint.x - viewport.getCenterX());
double dCY = (mouseControlPoint.y + viewport.getCenterY() - viewport.getHeight());
final double stepPx = 10;
// As the Earth is curved and we are not guaranteed to have a frontal view on it
// latitude an longitude lines are not really parallel to X or Y. But we assume that
// locally they are parallel enough both around the mousePoint and around the center.
// So we use reference points near center to calculate how we want to move the center.
Vec4 controlPointRight = new Vec4(viewport.getCenterX() + stepPx, viewport.getCenterY());
Vec4 geoPointRight = orbitView.unProject(controlPointRight);
Position positionRight = (geoPointRight != null) ? orbitView.getGlobe().computePositionFromPoint(geoPointRight) : null;
Vec4 controlPointUp = new Vec4(viewport.getCenterX(), viewport.getCenterY() - stepPx);
Vec4 geoPointUp = orbitView.unProject(controlPointUp);
Position positionUp = (geoPointUp != null) ? orbitView.getGlobe().computePositionFromPoint(geoPointUp) : null;
Position centerPosition = orbitView.getCenterPosition();
double newCenterLongDeg;
if (Math.abs(dCX) <= 1.0) // same X => same longitude
{
newCenterLongDeg = mouseGeoLocation.longitude.degrees;
}
else if (positionRight == null) // if controlPointRight is outside of the globe - don't try to fix this coordinate
{
newCenterLongDeg = centerPosition.longitude.degrees;
}
else
{
double scaleX = -dX / stepPx;
// apply acceleration if possible
int dXSign = sign(dX);
double accScaleX = scaleX * calcAccelerationK(dXSign, lastDxSign);
lastDxSign = dXSign;
newCenterLongDeg = centerPosition.longitude.degrees * (1 - accScaleX) + positionRight.longitude.degrees * accScaleX;
// if we overshot - use non-accelerated mode
if (!isBetween(newCenterLongDeg, centerPosition.longitude.degrees, mouseGeoLocation.longitude.degrees)
|| !isBetween(newCenterLongDeg, -180, 180))
{
newCenterLongDeg = centerPosition.longitude.degrees * (1 - scaleX) + positionRight.longitude.degrees * scaleX;
}
}
double newCenterLatDeg;
if (Math.abs(dCY) <= 1.0) // same Y => same latitude
{
newCenterLatDeg = mouseGeoLocation.latitude.degrees;
}
else if (positionUp == null) // if controlPointUp is outside of the globe - don't try to fix this coordinate
{
newCenterLatDeg = centerPosition.latitude.degrees;
}
else
{
double scaleY = -dY / stepPx;
// apply acceleration if possible
int dYSign = sign(dY);
double accScaleY = scaleY * calcAccelerationK(dYSign, lastDySign);
lastDySign = dYSign;
newCenterLatDeg = centerPosition.latitude.degrees * (1 - accScaleY) + positionUp.latitude.degrees * accScaleY;
// if we overshot - use non-accelerated mode
if (!isBetween(newCenterLatDeg, centerPosition.latitude.degrees, mouseGeoLocation.latitude.degrees)
|| !isBetween(newCenterLatDeg, -90, 90))
{
newCenterLatDeg = centerPosition.latitude.degrees * (1 - scaleY) + positionUp.latitude.degrees * scaleY;
}
}
Position newCenterPosition = Position.fromDegrees(newCenterLatDeg, newCenterLongDeg);
orbitView.setCenterPosition(newCenterPosition);
}
if (!zoomAnimator.hasNext())
stop();
}
private void nextWithTilt()
{
stepNumber++;
if (!zoomAnimator.hasNext() || (stepsNoAdjustments > 20))
{
System.out.println("Stop after " + stepNumber);
stop();
}
Vec4 curProjection = orbitView.project(mouseGeoPoint);
Rectangle viewport = orbitView.getViewport();
System.out.println("----------------------------------");
System.out.println("Mouse: mouseControlPoint = " + mouseControlPoint + "\t location = " + mouseGeoLocation + "\t viewSize = " + viewport);
System.out.println("Mouse: curProjection = " + curProjection);
double dX = (mouseControlPoint.x - curProjection.x);
double dY = (viewport.getHeight() - mouseControlPoint.y - curProjection.y); // Y is inverted
Vec4 dTgt = new Vec4(dX, dY);
// sometimes if you zoom close to the edge curProjection is calculated as somewhere
// way beyond where it is and it leads to overflow. This is a protection against it
if (Math.abs(dX) > viewport.width / 4 || Math.abs(dY) > viewport.height / 4)
{
Vec4 unproject = orbitView.unProject(new Vec4(mouseControlPoint.x, viewport.getHeight() - mouseControlPoint.y));
System.out.println("!!!End Mouse:"
+ " dX = " + dX + "\t" + " dY = " + dY
+ "\n" + "unprojectPt = " + unproject
+ "\n" + "unprojectPos = " + orbitView.getGlobe().computePositionFromPoint(unproject)
);
stepsNoAdjustments += 1;
return;
}
if (Math.abs(dX) <= EPS && Math.abs(dY) <= EPS)
{
stepsNoAdjustments += 1;
System.out.println("Mouse: No adjustment: " + " dX = " + dX + "\t" + " dY = " + dY);
return;
}
else
{
stepsNoAdjustments = 0;
}
// create reference points about 10px away from the center to the Up and to the Right
// and then map them to screen coordinates and geo coordinates
// Unfortunately unproject often generates points far from the Earth surface (and
// thus with significantly less difference in lat/long)
// So this longer but more fool-proof calculation is used
final double stepPx = 10;
Position centerPosition = orbitView.getCenterPosition();
Position eyePosition = orbitView.getEyePosition();
double pixelGeoSize = orbitView.computePixelSizeAtDistance(eyePosition.elevation - centerPosition.elevation);
Vec4 geoCenterPoint = orbitView.getCenterPoint();
Vec4 geoRightPoint = geoCenterPoint.add3(new Vec4(pixelGeoSize * stepPx, 0, 0));
Vec4 geoUpPoint = geoCenterPoint.add3(new Vec4(0, pixelGeoSize * stepPx, 0));
Position geoRightPosition = orbitView.getGlobe().computePositionFromPoint(geoRightPoint);
Position geoUpPosition = orbitView.getGlobe().computePositionFromPoint(geoUpPoint);
Vec4 controlCenter = orbitView.project(geoCenterPoint);
Vec4 controlRight = orbitView.project(geoRightPoint);
Vec4 controlUp = orbitView.project(geoUpPoint);
Vec4 controlRightDif = controlRight.subtract3(controlCenter);
controlRightDif = new Vec4(controlRightDif.x, controlRightDif.y); // ignore z for scale calculation
Vec4 controlUpDif = controlUp.subtract3(controlCenter);
controlUpDif = new Vec4(controlUpDif.x, controlUpDif.y); // ignore z for scale calculation
double scaleRight = -dTgt.dot3(controlRightDif) / controlRightDif.getLengthSquared3();
double scaleUp = -dTgt.dot3(controlUpDif) / controlUpDif.getLengthSquared3();
Position posRightDif = geoRightPosition.subtract(centerPosition);
Position posUpDif = geoUpPosition.subtract(centerPosition);
double totalLatDifDeg = posRightDif.latitude.degrees * scaleRight + posUpDif.latitude.degrees * scaleUp;
double totalLongDifDeg = posRightDif.longitude.degrees * scaleRight + posUpDif.longitude.degrees * scaleUp;
Position totalDif = Position.fromDegrees(totalLatDifDeg, totalLongDifDeg);
// don't copy elevation!
Position newCenterPosition = Position.fromDegrees(centerPosition.latitude.degrees + totalLatDifDeg,
centerPosition.longitude.degrees + totalLongDifDeg);
// if we overshot - try to slow down
if (!isValid(newCenterPosition))
{
newCenterPosition = Position.fromDegrees(centerPosition.latitude.degrees + totalLatDifDeg / 2,
centerPosition.longitude.degrees + totalLongDifDeg / 2);
if (!isValid(newCenterPosition))
{
System.out.println("Too much overshot: " + newCenterPosition);
stepsNoAdjustments += 1;
return;
}
}
System.out.println("Mouse:"
+ " dX = " + dX + "\t" + " dY = " + dY
+ "\n"
+ " centerPosition = " + centerPosition
+ "\n"
+ " geoUpPoint = " + geoUpPoint + "\t " + " geoUpPosition = " + geoUpPosition
+ "\n"
+ " geoRightPoint = " + geoRightPoint + "\t " + " geoRightPosition = " + geoRightPosition
+ "\n"
+ " posRightDif = " + posRightDif
+ "\t"
+ " posUpDif = " + posUpDif
+ "\n"
+ " scaleRight = " + scaleRight + "\t" + " scaleUp = " + scaleUp);
System.out.println("Mouse: oldCenterPosition = " + centerPosition);
System.out.println("Mouse: newCenterPosition = " + newCenterPosition);
orbitView.setCenterPosition(newCenterPosition);
}
}
Update
FixZoomPositionAnimator was updated to take into account the fact that one a large scale you can't assume that longitude and latitude lines go parallel to X and Y. To work this around reference points around the center are used to calculate adjustment. This means that the code will not work if the globe size is less than about 20px (2*stepPx) or if the user has tilted the Earth making latitude/longitude significantly non-parallel to X/Y.
End of Update
Update #2
I've moved previous logic to nextOld and added nextWithTilt. The new function should work even if the world is tilted but as the base logic is more complicated now, there is no acceleration anymore which IMHO makes it a bit worse for more typical cases. Also there are still a log of logging inside nextWithTilt because I'm not quite sure it really works OK. Use at your own risk.
End of Update #2
and then you may use it as
public class ZoomToCursorViewInputHandler extends OrbitViewInputHandler
{
public ZoomToCursorViewInputHandler()
{
ViewInputAttributes.ActionAttributes actionAttrs = this.getAttributes()
.getActionMap(ViewInputAttributes.DEVICE_MOUSE_WHEEL)
.getActionAttributes(ViewInputAttributes.VIEW_VERTICAL_TRANSLATE);
actionAttrs.setMouseActionListener(new ZoomActionHandler());
}
protected class ZoomActionHandler extends VertTransMouseWheelActionListener
{
#Override
public boolean inputActionPerformed(AbstractViewInputHandler inputHandler, MouseWheelEvent mouseWheelEvent,
final ViewInputAttributes.ActionAttributes viewAction)
{
double zoomInput = mouseWheelEvent.getWheelRotation();
Position position = wwd.getCurrentPosition();
Point mouseControlPoint = mouseWheelEvent.getPoint();
// Zoom toward the cursor if we're zooming in. Move straight out when zooming
// out.
if (zoomInput < 0 && position != null)
{
boolean res = super.inputActionPerformed(inputHandler, mouseWheelEvent, viewAction);
BasicOrbitView view = (BasicOrbitView) getView();
OrbitViewMoveToZoomAnimator zoomAnimator = (OrbitViewMoveToZoomAnimator) uiAnimControl.get(VIEW_ANIM_ZOOM);
// for continuous scroll preserve the original target if mouse was not moved
FixZoomPositionAnimator old = (FixZoomPositionAnimator) uiAnimControl.get(FixZoomPositionAnimator.VIEW_ANIM_KEY);
if (old != null && old.getMouseControlPoint().equals(mouseControlPoint))
{
position = old.getMouseGeoLocation();
}
FixZoomPositionAnimator fixZoomPositionAnimator = new FixZoomPositionAnimator(view, zoomAnimator, mouseControlPoint, position);
fixZoomPositionAnimator.start();
uiAnimControl.put(FixZoomPositionAnimator.VIEW_ANIM_KEY, fixZoomPositionAnimator);
return res;
}
else
{
uiAnimControl.remove(FixZoomPositionAnimator.VIEW_ANIM_KEY); // when zoom direction changes we don't want to make position adjustments anymore
return super.inputActionPerformed(inputHandler, mouseWheelEvent, viewAction);
}
}
}
// here goes aforementioned FixZoomPositionAnimator
}
I'm looking for some assistance with solving the below equations in Java
(a-x1)^2 + (b-y1)^2 = r1^2 + r^2
(a-x2)^2 + (b-y2)^2 = r2^2 + r^2
(a-x3)^2 + (b-y3)^2 = r3^2 + r^2
Values of x1, y1, r1, x2, y2, r2 & x3, y3, r3 are known.
I need to solve for a, b, r
How to go about doing this in Java? I checked the Commons Maths library but didn't find how I could achieve this. It helps with linear equations though.
I think you need linear equations for Gaussian elimination.
If a, b, and r are what you need to solve for, it's obvious that these are non-linear equations.
You'll need a non-linear solver, like Newton-Raphson.
You'll have to linearize your equations. Calculate the Jacobean for the differentials da, db, and dr.
You'll start with an initial guess
a = a(old)
b = b(old)
r = r(old)
use a linearized version of the equations to calculate an increment
2*(a(old)-x1)*da + 2*(b(old)-y1)*db = 2*r(old)*dr
2*(a(old)-x2)*da + 2*(b(old)-y2)*db = 2*r(old)*dr
2*(a(old)-x3)*da + 2*(b(old)-y3)*db = 2*r(old)*dr
update your guess
a(new) = a(old) + da
b(new) = b(old) + db
r(new) = r(old) + dr
and repeat until it converges (if it converges).
You should never solve linear equations using Gaussian elimination: it suffers from a number of problems. A better idea is to do LU decomposition and forward-back substitution.
If my linearized equations are correct, they take the form A(dx) = 0. What should the boundary condition be?
(a, b) are the coordinates for the center of the circle; r is the radius.
Do you really have three points (x1, y1), (x2, y2), and (x3, y3)? Or do you have lots more points? If it's the latter, you'll need a least squares fit.
hope this method can give you some ideas:
public int[] getCoordinates(float XR_1, float YR_1, float XR_2, float YR_2,
float XR_3, float YR_3, int R1, int R2, int R3) {
//define the positions
int XU_1 = 0, YU_1 = 0, XU_2 = 0, YU_2 = 0, XU, YU;
//define variables and arrays that needed
float D0[][] = new float[17][50];
float D1[][] = new float[17][50];
float f[][] = new float[17][50];
float fmin_1 = 0;
float fmin_2 = 0;
//define columns and rows
int i, j;
//Y goes from 0 to 49
for(j=0; j<=49; j++){
//X goes from 0 to 16
for(i=0; i<=16; i++){
D0[i][j] = (float) (Math.pow((i-XR_1),2) + Math.pow((j-YR_1),2) - Math.pow(R1,2));
D1[i][j] = (float) (Math.pow((i-XR_2),2) + Math.pow((j-YR_2),2) - Math.pow(R2,2));
f[i][j] = (float) Math.sqrt(Math.pow(D0[i][j], 2) + Math.pow(D1[i][j], 2));
//get two position where f[i][j] are the minimum
//initialise the minimum two positions
if(i==0 & j==0){
fmin_1 = f[i][j];
XU_1 = i;
YU_1 = j;
}
else if(j==0 & i==1){
if(f[i][j] < fmin_1){
fmin_2 = fmin_1;
fmin_1 = f[i][j];
XU_2 = XU_1;
XU_1 = i;
YU_2 = YU_1;
YU_1 = j;
}
else {
fmin_2 = f[i][j];
XU_2 = i;
YU_2 = j;
}
}
else{
if(f[i][j] < fmin_1){
fmin_2 = fmin_1;
fmin_1 = f[i][j];
XU_2 = XU_1;
XU_1 = i;
YU_2 = YU_1;
YU_1 = j;
}
else if(f[i][j] < fmin_2){
fmin_2 = f[i][j];
XU_2 = i;
YU_2 = j;
}
}
}
}
this method gives two closest points in the coordinate system, you can use the similar way to get the most ideal one.
Solved, used this code:
if ( !isClockwise(TempVectArray) ) { Collections.reverse(TempVectArray); }
...
private boolean isClockwise(ArrayList<Vec2> arl){
Iterator<Vec2> it = arl.iterator();
Vec2 pt1 = (Vec2)it.next();
Vec2 firstPt = pt1;
Vec2 lastPt = null;
double area = 0.0;
while(it.hasNext()){
Vec2 pt2 = (Vec2) it.next();
area += (((pt2.x - pt1.x) * (pt2.y + pt1.y)) / 2);
pt1 = pt2;
lastPt = pt1;
}
area += (((firstPt.x - lastPt.x) * (firstPt.y + lastPt.y)) / 2);
return area < 0;
}
Suppose I get a vertex array from the user tapping on the screen, but need it to be clockwise.
Maybe you know of some standard methods to check if it is clockwise and if it's not, then make it clockwise?
Thanks!
One way to do it is to first calculate the average point, and then sort everything around it by angle. Should be something like this:
public static void sortPointsClockwise(ArrayList<PointF> points) {
float averageX = 0;
float averageY = 0;
for (PointF point : points) {
averageX += point.x;
averageY += point.y;
}
final float finalAverageX = averageX / points.size();
final float finalAverageY = averageY / points.size();
Comparator<PointF> comparator = new Comparator<PointF>() {
public int compare(PointF lhs, PointF rhs) {
double lhsAngle = Math.atan2(lhs.y - finalAverageY, lhs.x - finalAverageX);
double rhsAngle = Math.atan2(rhs.y - finalAverageY, rhs.x - finalAverageX);
// Depending on the coordinate system, you might need to reverse these two conditions
if (lhsAngle < rhsAngle) return -1;
if (lhsAngle > rhsAngle) return 1;
return 0;
}
};
Collections.sort(points, comparator);
}
public static void sortPointsCounterClockwise(ArrayList<PointF> points) {
sortPointsClockwise(points);
Collections.reverse(points);
}
You have the sequence numbers and positions of the nodes. Get the movements which hold x and y changes in the move. All left to do is define a control structure such as:
if(movement_before is "up")
movement should-be "up" or "up-right"
if(movement_before is "up-left")
movement should-be "up" or "up-left" or "up-right"
etc..
I'm trying to do some advanced features with android maps and to do that I need to do some operations on vectors. Now - I read the answer from this and it gave me some hints and tips. However, there is a part which I don't understand. Please allow me to quote this:
Now that we have the ray with its start and end coordinates, the problem shifts from "is the point within the polygon" to "how often intersects the ray a polygon side". Therefor we can't just work with the polygon points as before (for the bounding box), now we need the actual sides. A side is always defined by two points.
side 1: (X1/Y1)-(X2/Y2) side 2:
(X2/Y2)-(X3/Y3) side 3:
(X3/Y3)-(X4/Y4)
So my understanding is that every side of the triangle is actually a vector. But how is it possible to substract 2 points? Let's say I got a triangle with 3 vertices: A(1,1) , B(2,2), C (1,3). So according to that, I have to do, for example, (1,1)-(2,2) in order to calculate one of the sides. The question is how to do it programatically in java/android? Below I'm attaching the code which I already developed:
/** Creating the containers for screen
* coordinates taken from geoPoints
*/
Point point1_screen = new Point();
Point point2_screen = new Point();
Point point3_screen = new Point();
/* Project them from the map to screen */
mapView.getProjection().toPixels(point1, point1_screen);
mapView.getProjection().toPixels(point2, point2_screen);
mapView.getProjection().toPixels(point3, point3_screen);
int xA = point1_screen.x;
int yA = point1_screen.y;
int xB = point2_screen.x;
int yB = point2_screen.y;
int xC = point3_screen.x;
int yC = point3_screen.y;
int[] xPointsArray = new int[3];
int[] yPointsArray = new int[3];
xPointsArray[0] = xA;
xPointsArray[1] = xB;
xPointsArray[2] = xC;
yPointsArray[0] = yA;
yPointsArray[1] = yB;
yPointsArray[2] = yC;
Arrays.sort(xPointsArray);
int xMin = xPointsArray[0];
int yMin = yPointsArray[0];
int xMax = xPointsArray[xPointsArray.length-1];
int yMax = xPointsArray[xPointsArray.length-1];
int e = (xMax - xMin) / 100; // for ray calcultions
int width = mapView.getWidth();
int height = mapView.getHeight();
if(pPoint.x < xMin || pPoint.x > xMax || pPoint.y > yMin || pPoint.y < yMax)
{
DisplayInfoMessage(pPoint.x + " < " + xMin + " AND " + pPoint.x + " > " + xMax + " || " + pPoint.y + " < " + yMin + " AND " + pPoint.y + " > " + yMax );
// DisplayInfoMessage("Minimum is: "+ yPointsArray[0] + " and the maximum is: "+ yPointsArray[xPointsArray.length-1]);
}
else
{
GeoPoint start_point = new GeoPoint(xMin - e, pPoint.y);
Point start_point_container = new Point();
mapView.getProjection().toPixels(start_point, start_point_container);
int a, b, c, tx, ty;
int d1, d2, hd;
int ix, iy;
float r;
// calculating vector for 1st line
tx = xB - xA;
ty = yB - yA;
// equation for 1st line
a = ty;
b = tx;
c = xA*a - yA*b;
// get distances from line for line 2
d1 = a*xB + b*yB + c;
d2 = a*pPoint.x + b*pPoint.y + c;
DisplayInfoMessage("You clicked inside the triangle!" + "TRIANGLE POINTS: A("+xA+","+yA+") B("+xB+","+yB+") C("+xC+","+yC+")");
}
The pPoint hold the coordinates of the point which user clicked. I hope that I explained my problem well enough. Can someone give me some help with that? Appreciated!
I'm not an Android developer, but I see that android.graphics.drawable.shapes.Shape lacks the contains() method found in java.awt.Shape. It appears you'll have to develop your own test, as suggested in the article you cited. In addition, you might want to look at crossing/winding number algorithms.
But how is it possible to subtract 2 points?
Subtraction of vectors is well defined, and easily implemented in Java. Given two points as vectors, the components of the difference represent the tangent (slope) of a line connecting the points. The example in the article implements this in the following lines:
//get tangent vector for line 1
tx = v1x2 - v1x1;
ty = v1y2 - v1y1;
The foundation for the approach shown is discussed further in Line and Segment Intersections.