[Android Apps and Games] Tamasaka:
Curved arrows

This script draws arrows with curved shaft substituted by 1/4 elliptic arc. As same as the above straight version, this script has five parameters as follows.

1. Size of arrow head.

2. Tip angle of arrow head in degrees.

3. Select shape of head from a line head, a barbed head or a delta head. Barbed head and delta head arrows are drawn with butt stroke cap.

4. Single head or double head.

5. Free or snap to grid.

/* Arrow (1/4 arc, multi-shape) ver. 1.01 */
headsize = 4;
tipangle = 45;
headshape = 1;
heads = 1;
snap = 0;
arclimit = 1;
if (LN.getNumberOfParameters() >= 1)
headsize = LN.getParameter(1); /* Head size, 2 ≤ Parameter(1) ≤ 8, 7 steps */
if (LN.getNumberOfParameters() >= 2)
tipangle = LN.getParameter(2); /* Tip angle (degree), 30 ≤ Parameter(2) ≤ 120, 7 steps */
if (LN.getNumberOfParameters() >= 3)
headshape = LN.getParameter(3); /* Head shape (0:line, 1:barbed, 2:delta), 0 ≤ Parameter(3) ≤ 2, 3 steps */
if (LN.getNumberOfParameters() >= 4)
heads = LN.getParameter(4); /* Heads (2:double), 1 ≤ Parameter(4) ≤ 2, 2 steps */
if (LN.getNumberOfParameters() >= 5)
snap = LN.getParameter(5); /* Snap to grid (1:on), 0 ≤ Parameter(5) ≤ 1, 2 steps */

strokewidth = LN.getStrokeWidth();
theta = tipangle * Math.PI / 360;
headlength = (headsize + 1) / 2 * strokewidth / Math.sqrt(Math.sin(theta));

x1 = LN.getX1();
y1 = LN.getY1();
x2 = LN.getX2();
y2 = LN.getY2();

if (snap) {
width = LN.getWidth();
scale = LN.getScale();
step = width / 150 + scale * width / 25;
x1 = (step / 2) + Math.floor((x1 - (step / 2)) / step + 0.5) * step;
y1 = (step / 2) + Math.floor((y1 - (step / 2)) / step + 0.5) * step;
x2 = (step / 2) + Math.floor((x2 - (step / 2)) / step + 0.5) * step;
y2 = (step / 2) + Math.floor((y2 - (step / 2)) / step + 0.5) * step;
}

xgap = (x2 - x1) / headlength / Math.cos(theta);
ygap = (y2 - y1) / headlength / Math.cos(theta);

if (xgap > arclimit)
headdirection = Math.PI;
else if (xgap < -1 * arclimit)
headdirection = 0;
else {
if (ygap > 0)
headdirection = Math.PI*3/2;
else
headdirection = Math.PI/2;
}

if (ygap > arclimit)
taildirection = Math.PI/2;
else if (ygap < -1 * arclimit)
taildirection = Math.PI*3/2;
else {
if (xgap > 0)
taildirection = 0;
else
taildirection = Math.PI;
}

if (xgap > 0) {
if (ygap> 0)
arcinitial = Math.PI/2;
else
arcinitial = Math.PI;
}
else {
if (ygap > 0)
arcinitial = 0;
else
arcinitial = Math.PI*3/2;
}

x21 = x2 + headlength * Math.cos(headdirection + theta);
y21 = y2 + headlength * Math.sin(headdirection + theta);
x22 = x2 + headlength * Math.cos(headdirection - theta);
y22 = y2 + headlength * Math.sin(headdirection - theta);
x11 = x1 + headlength * Math.cos(taildirection + theta);
y11 = y1 + headlength * Math.sin(taildirection + theta);
x12 = x1 + headlength * Math.cos(taildirection - theta);
y12 = y1 + headlength * Math.sin(taildirection - theta);
xmin = Math.min(Math.min(x1, Math.min(x11, x12)), Math.min(x2, Math.min(x21, x22)));
ymin = Math.min(Math.min(y1, Math.min(y11, y12)), Math.min(y2, Math.min(y21, y22)));
xmax = Math.max(Math.max(x1, Math.max(x11, x12)), Math.max(x2, Math.max(x21, x22)));
ymax = Math.max(Math.max(y1, Math.max(y11, y12)), Math.max(y2, Math.max(y21, y22)));

if (headshape >= 2) {
x2m = (x21 + x22) / 2;
y2m = (y21 + y22) / 2;
x1m = (x11 + x12) / 2;
y1m = (y11 + y12) / 2;
}
else {
x2m = (x21 * 2 + x22 * 2 + x2) / 5;
y2m = (y21 * 2 + y22 * 2 + y2) / 5;
x1m = (x11 * 2 + x12 * 2 + x1) / 5;
y1m = (y11 * 2 + y12 * 2 + y1) / 5;
}

if (heads == 1) {
x1m = x1;
y1m = y1;
}

LN.setStrokeCap(headshape?1:0);
LN.setClip(xmin, ymin, xmax, ymax);
if (headshape ==0) {
LN.drawLine (x2, y2, x21, y21);
LN.drawLine (x2, y2, x22, y22);
LN.drawLine (x2, y2, x2m, y2m);
if (heads == 2) {
LN.drawLine (x1, y1, x11, y11);
LN.drawLine (x1, y1, x12, y12);
LN.drawLine (x1, y1, x1m, y1m);
}
}
else {
LN.drawFilledQuadrangle(x2, y2, x21, y21, x2m, y2m, x22, y22);
if (heads == 2)
LN.drawFilledQuadrangle(x1, y1, x11, y11, x1m, y1m, x12, y12);
}
xc = x2m;
xr = Math.abs(x1 - x2m);
if (heads == 2) {
yc = y1m;
yr = Math.abs(y1m - y2m);
}
else {
yc = y1;
yr = Math.abs(y1 - y2m);
}
if (Math.abs(xgap) > arclimit && Math.abs(ygap) > arclimit)
LN.drawArc(xc, yc, xr, yr, arcinitial, Math.PI/2);
else
LN.drawLine(x1m, y1m, x2m, y2m);