Java 2D Polygon outside another - graphics2d

I'd like to know if there is a java way to, given a polygon, draw another one at a given distance and with the same center.
I tried AffineTransform but don't really know how it Works.
Thank you.

You need to translate your polygon by half its centroid width and height. I have included the code that comes from http://paulbourke.net/geometry/polygonmesh/PolygonUtilities.java to calculate the centroid of a polygon.
public void drawPolygon(){
Graphics2D g2 = bufferedImage.createGraphics();
Polygon poly=new Polygon();
poly.addPoint(100, 100);
poly.addPoint(200, 100);
poly.addPoint(200, 200);
poly.addPoint(150, 250);
poly.addPoint(100, 200);
poly.addPoint(100, 100);
g2.setColor(Color.blue);
g2.fillPolygon(poly);
g2.setColor(Color.red);
Point2D.Double []pts=new Point2D.Double[poly.npoints];
for (int i=0;i<poly.npoints;i++){
pts[i]=new Point2D.Double(poly.xpoints[i],poly.ypoints[i]);
}
Point2D centroid=centerOfMass(pts);
g2.translate(-centroid.getX(), -centroid.getY());
g2.scale(2, 2);
g2.drawPolygon(poly);
}
public static double area(Point2D[] polyPoints) {
int i, j, n = polyPoints.length;
double area = 0;
for (i = 0; i < n; i++) {
j = (i + 1) % n;
area += polyPoints[i].getX() * polyPoints[j].getY();
area -= polyPoints[j].getX() * polyPoints[i].getY();
}
area /= 2.0;
return (area);
}
/**
* Function to calculate the center of mass for a given polygon, according
* to the algorithm defined at
* http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
*
* #param polyPoints
* array of points in the polygon
* #return point that is the center of mass
*/
public static Point2D centerOfMass(Point2D[] polyPoints) {
double cx = 0, cy = 0;
double area = area(polyPoints);
// could change this to Point2D.Float if you want to use less memory
Point2D res = new Point2D.Double();
int i, j, n = polyPoints.length;
double factor = 0;
for (i = 0; i < n; i++) {
j = (i + 1) % n;
factor = (polyPoints[i].getX() * polyPoints[j].getY()
- polyPoints[j].getX() * polyPoints[i].getY());
cx += (polyPoints[i].getX() + polyPoints[j].getX()) * factor;
cy += (polyPoints[i].getY() + polyPoints[j].getY()) * factor;
}
area *= 6.0f;
factor = 1 / area;
cx *= factor;
cy *= factor;
res.setLocation(cx, cy);
return res;
}
Another way of doing this, common in the GIS world, is to buffer a polygon. There is a library called Java Topology Suite that will provide this functionality, although it might be harder to figure out what the scale factor is.
There are some very interesting discussions about polygon growing in this post: An algorithm for inflating/deflating (offsetting, buffering) polygons

Related

Efficiently XOR two images in Flash compile target

I need to XOR two BitmapData objects together.
I'm writing in Haxe, using the flash.* libraries and the AS3 compile target.
I've investigated HxSL and PixelBender, and neither one seems to have a bitwise XOR operator, nor do they have any other bitwise operators that could be used to create XOR (but am I missing something obvious? I'd accept any answer which gives a way to do a bitwise XOR using only the integer/float operators and functions available in HxSL or PixelBlender).
None of the predefined filters or shaders in Flash that I can find seem to be able to do a XOR of two images (but again, am I missing something obvious? Can XOR be done with a combination of other filters).
I can find nothing like a XOR drawmode for drawing things onto other things (but that doesn't mean it doesn't exist! That would work too, if it exists!)
The only way I can find at the moment is a pixel-by-pixel loop over the image, but this takes a couple of seconds per image even on a fast machine, as opposed to filters, which I use for my other image processing operations, which are about a hundred times faster.
Is there any faster method?
Edit:
Playing around with this a bit more I found that removing the conditional and extra Vector access in the loop speeds it up by about 100ms on my machine.
Here's the previous XOR loop:
// Original Vector XOR code:
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = vec1[i] ^ vec2[i];
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
result[i] |= 0xFF000000;
}
}
Here is the updated XOR loop for the Vector solution:
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
alphaMask = 0xFF000000;
}
// Fewer Vector accessors makes it quicker:
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = alphaMask | (vec1[i] ^ vec2[i]);
}
Answer:
Here are the solutions that I've tested to XOR two images in Flash.
I found that the PixelBender solution is about 6-10 slower than doing it in straight ActionScript.
I don't know if it's because I have a slow algorithm or it's just the limits of trying to fake bitwise operations in PixelBender.
Results:
PixelBender: ~6500ms
BitmapData.getVector(): ~480-500ms
BitmapData.getPixel32(): ~1200ms
BitmapData.getPixels(): ~1200ms
The clear winner is use BitmapData.getVector() and then XOR the two streams of pixel data.
1. PixelBender solution
This is how I implemented the bitwise XOR in PixelBender, based on the formula given on Wikipedia: http://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
Here is a Gist of the final PBK: https://gist.github.com/Coridyn/67a0ff75afaa0163f673
On my machine running an XOR on two 3200x1400 images this takes about 6500-6700ms.
I first converted the formula to JavaScript to check that it was correct:
// Do it for each RGBA channel.
// Each channel is assumed to be 8bits.
function XOR(x, y){
var result = 0;
var bitCount = 8; // log2(x) + 1
for (var n = 0; n < bitCount; n++) {
var pow2 = pow(2, n);
var x1 = mod(floor(x / pow2), 2);
var y1 = mod(floor(y / pow2), 2);
var z1 = mod(x1 + y1, 2);
result += pow2 * z1;
}
console.log('XOR(%s, %s) = %s', x, y, result);
console.log('%s ^ %s = %s', x, y, (x ^ y));
return result;
}
// Split out these functions so it's
// easier to convert to PixelBender.
function mod(x, y){
return x % y;
}
function pow(x, y){
return Math.pow(x, y);
}
function floor(x){
return Math.floor(x);
}
Confirm that it's correct:
// Test the manual XOR is correct.
XOR(255, 85); // 170
XOR(170, 85); // 255
XOR(170, 170); // 0
Then I converted the JavaScript to PixelBender by unrolling the loop using a series of macros:
// Bitwise algorithm was adapted from the "mathematical equivalents" formula on Wikipedia:
// http://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
// Macro for 2^n (it needs to be done a lot).
#define POW2(n) pow(2.0, n)
// Slight optimisation for the zeroth case - 2^0 = 1 is redundant so remove it.
#define XOR_i_0(x, y) ( mod( mod(floor(x), 2.0) + mod(floor(y), 2.0), 2.0 ) )
// Calculations for a given "iteration".
#define XOR_i(x, y, i) ( POW2(i) * ( mod( mod(floor(x / POW2(i)), 2.0) + mod(floor(y / POW2(i)), 2.0), 2.0 ) ) )
// Flash doesn't support loops.
// Unroll the loop by defining macros that call the next macro in the sequence.
// Adapted from: http://www.simppa.fi/blog/category/pixelbender/
// http://www.simppa.fi/source/LoopMacros2.pbk
#define XOR_0(x, y) XOR_i_0(x, y)
#define XOR_1(x, y) XOR_i(x, y, 1.0) + XOR_0(x, y)
#define XOR_2(x, y) XOR_i(x, y, 2.0) + XOR_1(x, y)
#define XOR_3(x, y) XOR_i(x, y, 3.0) + XOR_2(x, y)
#define XOR_4(x, y) XOR_i(x, y, 4.0) + XOR_3(x, y)
#define XOR_5(x, y) XOR_i(x, y, 5.0) + XOR_4(x, y)
#define XOR_6(x, y) XOR_i(x, y, 6.0) + XOR_5(x, y)
#define XOR_7(x, y) XOR_i(x, y, 7.0) + XOR_6(x, y)
// Entry point for XOR function.
// This will calculate the XOR the current pixels.
#define XOR(x, y) XOR_7(x, y)
// PixelBender uses floats from 0.0 to 1.0 to represent 0 to 255
// but the bitwise operations above work on ints.
// These macros convert between float and int values.
#define FLOAT_TO_INT(x) float(x) * 255.0
#define INT_TO_FLOAT(x) float(x) / 255.0
XOR for each channel of the current pixel in the evaluatePixel function:
void evaluatePixel()
{
// Acquire the pixel values from both images at the current location.
float4 frontPixel = sampleNearest(inputImage, outCoord());
float4 backPixel = sampleNearest(diffImage, outCoord());
// Set up the output variable - RGBA.
pixel4 result = pixel4(0.0, 0.0, 0.0, 1.0);
// XOR each channel.
result.r = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.r), FLOAT_TO_INT(backPixel.r)) );
result.g = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.g), FLOAT_TO_INT(backPixel.g)) );
result.b = INT_TO_FLOAT ( XOR(FLOAT_TO_INT(frontPixel.b), FLOAT_TO_INT(backPixel.b)) );
// Return the result for this pixel.
dst = result;
}
ActionScript Solutions
2. BitmapData.getVector()
I found the fastest solution is to extract a Vector of pixels from the two images and perform the XOR in ActionScript.
For the same two 3200x1400 this takes about 480-500ms.
package diff
{
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.IBitmapDrawable;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
/**
* #author Coridyn
*/
public class BitDiff
{
/**
* Perform a binary diff between two images.
*
* Return the result as a Vector of uints (as used by BitmapData).
*
* #param image1
* #param image2
* #param ignoreAlpha
* #return
*/
public static function diffImages(image1: DisplayObject,
image2: DisplayObject,
ignoreAlpha: Boolean = true): Vector.<uint> {
// For simplicity get the smallest common width and height of the two images
// to perform the XOR.
var w: Number = Math.min(image1.width, image2.width);
var h: Number = Math.min(image1.height, image2.height);
var rect: Rectangle = new Rectangle(0, 0, w, h);
var vec1: Vector.<uint> = BitDiff.getVector(image1, rect);
var vec2: Vector.<uint> = BitDiff.getVector(image2, rect);
var resultVec: Vector.<uint> = BitDiff.diffVectors(vec1, vec2, ignoreAlpha);
return resultVec;
}
/**
* Extract a portion of an image as a Vector of uints.
*
* #param drawable
* #param rect
* #return
*/
public static function getVector(drawable: DisplayObject, rect: Rectangle): Vector.<uint> {
var data: BitmapData = BitDiff.getBitmapData(drawable);
var vec: Vector.<uint> = data.getVector(rect);
data.dispose();
return vec;
}
/**
* Perform a binary diff between two streams of pixel data.
*
* If `ignoreAlpha` is false then will not normalise the
* alpha to make sure the pixels are opaque.
*
* #param vec1
* #param vec2
* #param ignoreAlpha
* #return
*/
public static function diffVectors(vec1: Vector.<uint>,
vec2: Vector.<uint>,
ignoreAlpha: Boolean): Vector.<uint> {
var larger: Vector.<uint> = vec1;
if (vec1.length < vec2.length) {
larger = vec2;
}
var len: Number = Math.min(vec1.length, vec2.length),
result: Vector.<uint> = new Vector.<uint>(len, true);
var alphaMask = 0;
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
alphaMask = 0xFF000000;
}
// Assume same length.
for (var i: int = 0; i < len; i++) {
// XOR.
result[i] = alphaMask | (vec1[i] ^ vec2[i]);
}
if (vec1.length != vec2.length) {
// Splice the remaining items.
result = result.concat(larger.slice(len));
}
return result;
}
}
}
3. BitmapData.getPixel32()
Your current approach of looping over the BitmapData with BitmapData.getPixel32() gave a similar speed of about 1200ms:
for (var y: int = 0; y < h; y++) {
for (var x: int = 0; x < w; x++) {
sourcePixel = bd1.getPixel32(x, y);
resultPixel = sourcePixel ^ bd2.getPixel(x, y);
result.setPixel32(x, y, resultPixel);
}
}
4. BitmapData.getPixels()
My final test was to try iterating over two ByteArrays of pixel data (very similar to the Vector solution above). This implementation also took about 1200ms:
/**
* Extract a portion of an image as a Vector of uints.
*
* #param drawable
* #param rect
* #return
*/
public static function getByteArray(drawable: DisplayObject, rect: Rectangle): ByteArray {
var data: BitmapData = BitDiff.getBitmapData(drawable);
var pixels: ByteArray = data.getPixels(rect);
data.dispose();
return pixels;
}
/**
* Perform a binary diff between two streams of pixel data.
*
* If `ignoreAlpha` is false then will not normalise the
* alpha to make sure the pixels are opaque.
*
* #param ba1
* #param ba2
* #param ignoreAlpha
* #return
*/
public static function diffByteArrays(ba1: ByteArray,
ba2: ByteArray,
ignoreAlpha: Boolean): ByteArray {
// Reset position to start of array.
ba1.position = 0;
ba2.position = 0;
var larger: ByteArray = ba1;
if (ba1.bytesAvailable < ba2.bytesAvailable) {
larger = ba2;
}
var len: Number = Math.min(ba1.length / 4, ba2.length / 4),
result: ByteArray = new ByteArray();
// Assume same length.
var resultPixel:uint;
for (var i: uint = 0; i < len; i++) {
// XOR.
resultPixel = ba1.readUnsignedInt() ^ ba2.readUnsignedInt();
if (ignoreAlpha) {
// Force alpha of FF so we can see the result.
resultPixel |= 0xFF000000;
}
result.writeUnsignedInt(resultPixel);
}
// Seek back to the start.
result.position = 0;
return result;
}
There are a few possible options depending on what you want to achieve (e.g. is the XOR per channel or is it just any pixel that is non-black?).
There is the BitmapData.compare() method which can give you a lot of information about the two bitmaps. You could BitmapData.threshold() the input data before comparing.
Another option would be to use the draw method with the BlendMode.DIFFERENCE blend mode to draw your two images into the same BitmapData instance. That will show you the difference between the two images (equivalent to the Difference blending mode in Photoshop).
If you need to check if any pixel is non-black then you can try running a BitmapData.threshold first and then draw the result with the difference blend mode as above for the two images.
Are you doing this for image processing or something else like per-pixel hit detection?
To start with I'd have a look at BitmapData and see what is available to play with.

ideal lowpass filter with fftw

again I am still trying to get my lowpass filter running, but I am at a point where I do not know why this is still not running. I oriented my code according to FFT Filters and my previous question FFT Question in order to apply an ideal low pass filter to the image. The code below just makes the image darker and places some white pixels in the resulting image.
// forward fft the result is in freqBuffer
fftw_execute(forward);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
uint gid = y * w + x;
// shifting coordinates normalized to [-0.5 ... 0.5]
double xN = (x - (w / 2)) / (double)w;
double yN = (y - (h / 2)) / (double)h;
// max radius
double maxR = sqrt(0.5f * 0.5f + 0.5f * 0.5f);
// current radius normalized to [0 .. 1]
double r = sqrt(xN * xN + yN * yN) / maxR ;
// filter response
double filter = r > 0.7f ? 0.0f : 1.0f;
// applying filter response
freqBuffer[gid][0] *= filter;
freqBuffer[gid][1] *= filter;
}
}
// normlization (see fftw scaling)
for (uint i = 0; i < size; i++)
{
freqBuffer[i][0] /= (float)size;
freqBuffer[i][1] /= (float)size;
}
// backward fft
fftw_execute(backward);
Some help would be appreciated.
Wolf
If you have a filter with a step response in the frequency domain then you will see significant sin(x)/x ringing in the spatial domain. This is known as the Gibbs Phenomenon. You need to apply a window function to the desired frequency response to mitigate this.

Algorithm for particles targeting

I'm building a particles systems, one of the features I'd like to add is a "target" feature. What I want to be able to do is set an X,Y target for each particle and make it go there, not in a straight line though (duh), but considering all other motion effects being applied on the particle.
The relevant parameters my particles have:
posx, posy : inits with arbitrary values. On each tick speedx and speedy are added to posx and posy respectively
speedx, speedy : inits with arbitrary values. On each tick accelx and accely are added to speedx speedy respectively if any)
accelx, accely : inits with arbitrary values. With current implementation stays constant through the lifespan of the particle.
life : starts with an arbitrary value, and 1 is reduced with each tick of the system.
What I want to achieve is the particle reaching the target X,Y on it's last life tick, while starting with it's original values (speeds and accelerations) so the motion towards the target will look "smooth". I was thinking of accelerating it in the direction of the target, while recalculating the needed acceleration force on each tick. That doesn't feel right though, would love to hear some suggestions.
For a "smooth" motion, you either keep the speed constant, or the acceleration constant, or the jerk constant. That depends on what you call "smooth" and what you call "boring". Let's keep the acceleration constant.
From a physics point of view, you have this constraint
targetx - posx = speedx*life + 1/2accelx * life * life
targety - posy = speedy*life + 1/2accely * life * life
Because distance traveled is v*t+1/2at^2. Solving for the unknown acceleration gives
accelx = (targetx - posx - speedx*life) / (1/2 * life * life)
accely = (targety - posy - speedy*life) / (1/2 * life * life)
(For this to work speedy must be in the same unit as time, for example "pixels per tick" and life is a number of "ticks". )
Since you use euler integration, this will not bring the particle exactly on the target. But I doubt it'll be a real issue.
Works like a charm:
Another picture, this time with constant jerk
jerkx = 6.0f*(targetx-x - speedx*life - 0.5f*accelx*life*life)/(life*life*life)
Looks like there is another bend in the curve...
Java code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class TargetTest extends JPanel {
List<Particle> particles = new ArrayList<Particle>();
float tx, ty; // target position
public TargetTest() {
tx = 400;
ty = 400;
for (int i = 0; i < 50; i++)
particles.add(new Particle(tx / 2 + (float) (tx * Math.random()), ty / 2
+ (float) (ty * Math.random())));
this.setPreferredSize(new Dimension((int) tx * 2, (int) ty * 2));
}
#Override
protected void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D) g1;
g.setColor(Color.black);
// comment next line to draw curves
g.fillRect(0, 0, getSize().width, getSize().height);
for (Particle p : particles) {
p.update();
p.draw(g);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Particle tracking");
final TargetTest world = new TargetTest();
f.add(world);
// 1 tick every 50 msec
new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
world.repaint();
}
}).start();
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
class Particle {
float x, y;// position
float vx, vy;// speed
float ax, ay;// acceleration
float jx, jy;// jerk
int life; // life
float lastx, lasty;// previous position, needed to draw lines
int maxlife; // maxlife, needed for color
public Particle(float x, float y) {
this.x = x;
this.y = y;
// pick a random direction to go to
double angle = 2 * Math.PI * Math.random();
setVelocity(angle, 2);// 2 pixels per tick = 2 pixels per 50 msec = 40
// pixels per second
// the acceleration direction 'should' be close to being perpendicular to
// the speed,
// makes it look interesting, try commenting it if you don't believe me ;)
if (Math.random() < 0.5)
angle -= Math.PI / 2;
else
angle += Math.PI / 2;
// add some randomness
angle += (Math.random() - 0.5) * Math.PI / 10;
setAcceleration(angle, 0.1);
life = (int) (100 + Math.random() * 100);
maxlife = life;
lastx = x;
lasty = y;
}
public void setVelocity(double angle, double speed) {
vx = (float) (Math.cos(angle) * speed);
vy = (float) (Math.sin(angle) * speed);
}
public void setAcceleration(double angle, double speed) {
ax = (float) (Math.cos(angle) * speed);
ay = (float) (Math.sin(angle) * speed);
}
#SuppressWarnings("unused")
private void calcAcceleration(float tx, float ty) {
ax = 2 * (tx - x - vx * life) / (life * life);
ay = 2 * (ty - y - vy * life) / (life * life);
}
private void calcJerk(float tx, float ty) {
jx = 6.0f * (tx - x - vx * life - 0.5f * ax * life * life)
/ (life * life * life);
jy = 6.0f * (ty - y - vy * life - 0.5f * ay * life * life)
/ (life * life * life);
}
public void update() {
lastx = x;
lasty = y;
if (--life <= 0)
return;
// calculate jerk
calcJerk(tx, ty);
// or uncomment and calculate the acceleration instead
// calcAcceleration(tx,ty);
ax += jx;
ay += jy;// increase acceleration
vx += ax;
vy += ay;// increase speed
x += vx;
y += vy;// increase position
}
public void draw(Graphics2D g) {
if (life < 0)
return;
g.setColor(new Color(255 - 255 * life / maxlife,
255 * life / maxlife,0));
g.drawLine((int) x, (int) y, (int) lastx, (int) lasty);
}
}
}
You could consider that your particule is initially "applied" a force (Fv) which corresponds to the inertia it has from its initial velocity. Then you apply an attraction force (Fa) that is proportionnal to the distance to the target. You can then sum those forces, and given a particle weight, you can deduce acceleration to consider at time t.
Fa(t) = (Constant / distanceToTarget(t))* [direction to target]
Fv(t) = [initialForce] * dampening(t)
a(t) = (Fa(t) + Fv(t)) / mass
Then you can compute v(t) from v(t-1) and a(t) as usual
Edit: I forgot the life of the particle can directly be computed from the distance to the target (for instance: life = distance / initialDistance will go from 1 at start and approch 0 near the target)
Edit: You could think of this as a kind of magnet. See wikipedia for the force formula.
one kind of movement you can use is the uniform acceleration http://en.wikipedia.org/wiki/Acceleration#Uniform_acceleration
Your particles will make a smoth move towards the target and hit it with rather high velocity
For meeting your stated criteria, do the following:
calculate the distance from the target, the particle will have at the end of it's life time, assuming the speed doesn't change from now on.
this distance put in this equation: http://upload.wikimedia.org/math/6/2/9/6295e1819e6bfe1101506caa4b4ec706.png and solve it for a
use this as your acceleration
Do this seperately for x and y

Algorithm to solve the points of a evenly-distributed / even-gaps spiral?

First, just to give a visual idea of what I'm after, here's the closest result (yet not exactly what I'm after) image that I've found:
Here's the entire site-reference: http://www.mathematische-basteleien.de/spiral.htm
BUT, it doesn't exactly solve the problem I'm after. I would like to store an array of points of a very specific spiral algorithm.
The points are evenly distributed
The 360 degree cycles have an even gap
If I'm not mistaken, the first two points would be:
point[ 0 ] = new Point(0,0);
point[ 1 ] = new Point(1,0);
But where to go from here?
The only arguments I'd like to provide are:
the quantity of points I wish to resolve (length of array).
the distance between each points (pixels gap).
the distance between cycles.
It almost sounds, to me, that I have to calculate the "spiral-circumference" (if there's such a term) in order to plot the evenly distributed points along the spiral.
Can 2*PI*radius be reliably used for this calculation you think?
If it's been done before, please show some code example!
Fun little problem :)
If you look at the diagram closer, the sequence is clearly stated:
There are probably many solutions to drawing these, maybe more elegant, but here's mine:
You know the hypotenuse is square root of the current segment count+1
and the opposite side of the triangle is always 1.
Also you know that Sine(Math.sin) of the angle is equal to the opposite side divided by the hypotenuse.
from the old mnenonic SOH(Sine,Opposite,Hypotenuse),-CAH-TOA.
Math.sin(angle) = opp/hyp
You know the value of the sine for the angle, you know the two sides, but you don't know the angle yet, but you can use the arc sine function(Math.asin) for that
angle = Math.asin(opp/hyp)
Now you know the angle for each segment, and notice it increments with each line.
Now that you have an angle and a radius(the hypotenuse) you can use for polar to cartesian formula to convert that angle,radius pair to a x,y pair.
x = Math.cos(angle) * radius;
y = Math.sin(angle) * radius;
Since you asked for an actionscript solution, there Point class already provides this function for you through the polar() method. You pass it a radius and angle and it returns your x and y in a Point object.
Here's a little snippet which plots the spiral. You can control the number of segments by moving the mouse on the Y axis.
var sw:Number = stage.stageWidth,sh:Number = stage.stageHeight;
this.addEventListener(Event.ENTER_FRAME,update);
function update(event:Event):void{
drawTheodorus(144*(mouseY/sh),sw*.5,sh*.5,20);
}
//draw points
function drawTheodorus(segments:int,x:Number,y:Number,scale:Number):void{
graphics.clear();
var points:Array = getTheodorus(segments,scale);
for(var i:int = 0 ; i < segments; i++){
points[i].offset(x,y);
graphics.lineStyle(1,0x990000,1.05-(.05+i/segments));
graphics.moveTo(x,y);//move to centre
graphics.lineTo(points[i].x,points[i].y);//draw hypotenuse
graphics.lineStyle(1+(i*(i/segments)*.05),0,(.05+i/segments));
if(i > 0) graphics.lineTo(points[i-1].x,points[i-1].y);//draw opposite
}
}
//calculate points
function getTheodorus(segments:int = 1,scale:Number = 10):Array{
var result = [];
var radius:Number = 0;
var angle:Number = 0;
for(var i:int = 0 ; i < segments ; i++){
radius = Math.sqrt(i+1);
angle += Math.asin(1/radius);//sin(angle) = opposite/hypothenuse => used asin to get angle
result[i] = Point.polar(radius*scale,angle);//same as new Point(Math.cos(angle)*radius.scale,Math.sin(angle)*radius.scale)
}
return result;
}
This could've been written in less lines, but I wanted to split this into two functions:
one that deals only with computing the numbers, and the other which deals with drawing the lines.
Here are some screenshots:
For fun I added a version of this using ProcessingJS here.
Runs a bit slow, so I would recommend Chromium/Chrome for this.
Now you can actually run this code right here (move the mouse up and down):
var totalSegments = 850,hw = 320,hh = 240,segments;
var len = 10;
points = [];
function setup(){
createCanvas(640,480);
smooth();
colorMode(HSB,255,100,100);
stroke(0);
noFill();
//println("move cursor vertically");
}
function draw(){
background(0);
translate(hw,hh);
segments = floor(totalSegments*(mouseY/height));
points = getTheodorus(segments,len);
for(var i = 0 ; i < segments ; i++){
strokeWeight(1);
stroke(255-((i/segments) * 255),100,100,260-((i/segments) * 255));
line(0,0,points[i].x,points[i].y);
// strokeWeight(1+(i*(i/segments)*.01));
strokeWeight(2);
stroke(0,0,100,(20+i/segments));
if(i > 0) line(points[i].x,points[i].y,points[i-1].x,points[i-1].y);
}
}
function getTheodorus(segments,len){
var result = [];
var radius = 0;
var angle = 0;
for(var i = 0 ; i < segments ; i++){
radius = sqrt(i+1);
angle += asin(1/radius);
result[i] = new p5.Vector(cos(angle) * radius*len,sin(angle) * radius*len);
}
return result;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
George's answer was excellent! I was looking for the solution for quite a while.
Here's the same code adjusted for PHP, in case it helps someone. I use the script to draw dots (= cities) for a map with X, Y coordinates. X starts from left, Y starts from bottom left.
<?
/**
* Initialize variables
**/
// MAXIMUM width & height of canvas (X: 0->400, Y: 0->400)
$width = 400;
// For loop iteration amount, adjust this manually
$segments = 10000;
// Scale for radius
$radiusScale = 2;
// Draw dot (e.g. a city in a game) for every N'th drawn point
$cityForEveryNthDot = 14;
/**
* Private variables
**/
$radius = 0;
$angle = 0;
$centerPoint = $width/2;
/**
* Container print
**/
print("<div style=\"width: ${width}px; height: ${width}px; background: #cdcdcd; z-index: 1; position: absolute; left: 0; top: 0;\"></div>");
/**
* Looper
**/
for($i=0;$i<$segments;$i++) {
// calculate radius and angle
$radius = sqrt($i+1) * $radiusScale;
$angle += asin(1/$radius);
// skip this point, if city won't be created here
if($i % $cityForEveryNthDot != 0) {
continue;
}
// calculate X & Y (from top left) for this point
$x = cos($angle) * $radius;
$y = sin($angle) * $radius;
// print dot
print("<div style=\"width: 1px; height: 1px; background: black; position: absolute; z-index: 2; left: " . round($x+$centerPoint) . "; top: " . round($y+$centerPoint) . ";\"></div>");
// calculate rounded X & Y (from bottom left)
$xNew = round($x+$centerPoint);
$yNew = round($width - ($y+$centerPoint));
// just some internal checks
if($xNew > 1 && $yNew > 1 && $xNew < $width && $yNew < $width) {
/**
* do something (e.g. store to database). Use xNew and yNew
**/
}
}

Google Map API: How to center dynamicly markers in AS3?

I have several markers on my map and want to center dynamily each time I click on a selected point which show a bunch of markers group.
Does anyone know how to do that in As3?
You could try to use the a formula to get the centroid of the polygon drawn by your markers, assuming it's a polygon. If not, and they're a bunch of scattered points, you need to get the ones on that form the outer bounding segments first.Also, the code assumes the polygon is closed(loops), so the last point is your first point again.
function centreOfMass(polyPoints:Array):Point{
var cx:Number = 0;
var cy:Number = 0;
var area:Number = area(polyPoints);
var result:Point = new Point();
var i:Number,j:Number,n:Number = polyPoints.length;
var factor:Number = 0;
for(i = 0; i < n ; i++){
j = (i+1) % n;
factor = polyPoints[i].x * polyPoints[j].y - polyPoints[j].x * polyPoints[i].y;
cx += polyPoints[i].x + polyPoints[j].x * factor;
cy += polyPoints[i].y + polyPoints[j].y * factor;
}
area *= 6.0;
factor = 1 / area;
cx *= factor;
cy *= factor;
result.offset(cx,cy);//sets x and y to cx and cy
return result;
}
function area(polyPoints:Array):Number{
var i:int,j:int,n:int = polyPoints.length;
var area:Number = 0;
for(i = 0; i < n; i++){
j = (i+1) % n;
area += polyPoints[i].x * polyPoints[j].y;
area -= polyPoints[j].x * polyPoints[i].y;
}
area *= 0.5;
return area;
}
You create an array of points and you use the lat/lon coords as x,y coords. If you're using flash player 10, feel free to change the array into a Vector. and don't forget to do the import.flash.geom.Point.
I didn't come up with the code, I just ported what was on the amazing Paul Bourke website. Tons of handy stuff there.