How could I replace fixed conditions to get clustering EPS for some calculation? - math

I use a function of DBSCAN for clustering, that takes the EPS value as a parameter (radius that defines which points will be grouped together).
To set the EPS I use the following switch case:
let eps = 0;
switch (true) {
case (zoom === 20):
eps = 0.00001;
break;
case (zoom > 18):
eps = 0.00002;
break;
case (zoom > 16):
eps = 0.0003;
break;
case (zoom > 14):
eps = 0.0014;
break;
case (zoom > 12):
eps = 0.0024;
break;
case (zoom > 10):
eps = 0.0082;
break;
case (zoom > 8):
eps = 0.032;
break;
case (zoom > 6):
eps = 0.2;
break;
case (zoom > 4):
eps = 0.45;
break;
default:
eps = 3;
}
How could I replace this switch case with fixed values ​​and conditions for some calculation?
I use this method to get current map zoom:
const handleRegionChange = ({ longitudeDelta, latitudeDelta }: Region) => {
const zoomLevel = Math.log(360 / Math.max(longitudeDelta, latitudeDelta)) / Math.LN2;
if (zoomLevel !== zoom) {
setZoom(Math.ceil(zoomLevel));
}
};
this method always returns a value from 1 to 20.

Use the convenient fact that the comparison operand decreases by 2 each time to calculate the index into a lookup array:
function getEpsFromZoom(zoom) {
if (zoom >= 20) {
return 0.00001;
} else {
const table = [ 3, 0.45, 0.2, 0.032, 0.0082, 0.0024, 0.0014, 0.0003, 0.00002 ];
const index = Math.ceil(zoom/2);
const clamp = Math.min(10, Math.max(2, index));
return table[clamp - 2];
}
}
Unfortunately 20 has to be handled separately here since its case condition is not a strict inequality like the others.

Related

How to create different iterations of 6 digit integer that is also 6 digits?

I am using an algorithm to create a 6 digit pin from a string of letters(I already have it). I also need to make different iterations of this 6 digit pin that would all lead back to the origin pin which can be used to generate the string of letters.
input "FEFOEISUDFRORI"
output 523923
some algorithm...
first iteration: 123203
then to authenticate
iteration: 1 ; pin: 123203
output: 'FEFOEISUDFRORI' // same as original string
Any idea how to do this?
The easiest way to solve this mathematical problem is probably with a rotation. Essentially performing an addition then a modulus, rotation will result in a one-to-one function with a range equal to it's domain.
The example I've shown will rotate the entire 6 digit number or the individual digits of the number.
function rRot(x, rot, max) {
if (rot < 0) return lRot(x,-rot,max);
rot = rot % max;
return (x + rot) % max;
}
function lRot(x, rot, max) {
if (rot < 0) return rRot(x,-rot,max);
rot = rot % max;
return rRot(x,max-rot,max);
}
function rotDigits(x, r) {
var pwr = 1, y = 0;
while (x > 0) {
var digit = x % 10;
y += rRot(digit, r, 10) * pwr;
x = Math.floor(x / 10);
pwr *= 10;
}
return y;
}
var samples = [675821, 126421, 678321, 100001, 580127, 999999];
(function () {
console.log("Rotate individual digits");
samples.forEach(v => {
var r = rotDigits(v, 7);
var vr = rotDigits(r, 10-7);
console.log(v.toString() + " => " + r.toString() + " => " + vr.toString());
});
console.log("Rotate whole number");
samples.forEach(v => {
var r = rRot(v, 65537, 1000000);
var vr = lRot(r, 65537, 1000000);
console.log(v.toString() + " => " + r.toString() + " => " + vr.toString());
});
})()

paperjs - How to apply brightness/contrast like effects on the raster image?

I have tried to apply the image adjustment options using paper.js, but it will only apply to the fillcolor.
Does anyone know how to apply brightness, contrast or other image adjustments to the raster image?
For example:
var url = 'http://images.com/q.jpg';
var raster = new paper.Raster(url);
raster.brightness = .5;
Are there any pre-defined functions available for image adjustment in paper.js?
Nope, but you can play with blend modes or opacity.
I would advise using specialized WebGL libraries like glfx or webgl-filter for image effects (I didn't try them, but they seem powerful).
function reDrawImage(lightness = 10,contrast = 1.5) {
const raster = paper.project.activeLayer.children[0] as paper.Raster
const ctx: CanvasRenderingContext2D = currentRaster.getContext(true)
const imageData = ctx.getImageData(0, 0, currentRaster.width, currentRaster.height)
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] = saturate_cast(imageData.data[i] * (contrast + lightness));
imageData.data[i+1] = saturate_cast(imageData.data[i+1] * (contrast + lightness));
imageData.data[i+2] = saturate_cast(imageData.data[i+2] * (contrast + lightness));
}
raster.setImageData(imageData, new Point(0, 0))
}
function saturate_cast(num: number) {
if (num > 255) {
return 255
}
if (num < 0) {
return 0
}
return num
}

WebGL Walkthrough, Move around the 3D scene

I'm new to WebGL, and I'm trying to create a walk-through for a website, I have taken my Maya model into WebGL with the help of inka3D, but when I apply the following code for the movement, it doesn't work as it explains. Only the left arrow works fine.
function resize()
{
var width = canvas.offsetWidth;
var height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
aspect = width / height;
}
var cameraTargetX = 37.2878151;
var cameraTargetY = 12.846137;
var cameraTargetZ = 7.17901707;
var dx = 5;
var dy = 5;
window.addEventListener('keydown',doKeyDown,true);
function doKeyDown(evt){
switch (evt.keyCode) {
case 38: /* Up arrow was pressed */
if (cameraTargetY - dy > 0){
cameraTargetY -= dy;
}
break;
case 40: /* Down arrow was pressed */
if (cameraTargetY + dy < height){
cameraTargetY += dy;
}
break;
case 37: /* Left arrow was pressed Fine*/
if (cameraTargetX - dx > 0){
cameraTargetX -= dx;
}
break;
case 39: /* Right arrow was pressed */
if (cameraTargetX + dx < width){
cameraTargetX += dx;
}
break;
}
}
};
If only the left arrow works this means that difference of (cameraTargetX - dx ) > 0. Thats why you can translate. The reason is cameraTargetX is 37 diff of 5 make it 32 and on key press you can visualize this in 5X7(loop). Key is pressed 7 times until the value become lesser than zero
But when var cameraTargetY = 12.846137; and dy is 5 it take only 5x2(loop) just a fraction and the value become lesser than zero and you can visualize the diff.
Solution is as stated dx and dy are delta values means this should be very small as variable convection so try with
var dx = 0.05;
var dy = 0.05;
You will get answer. If any doubt feel free to ask

Decrypting an Affine Cipher with Modulus

I'm trying to decrypt the ciphertext vczkh which I know was encoded using an affine cipher with the equation 7x + 8(mod 26). This makes my decryption function p = (c – b) * a^-1 (mod 26) where b = 8, a = 7, c = number corresponding with cipher character starting from 0, and p is the same for plaintext. Since I can't have a fraction I calculated that 11 is congruent to 7 making my function p = (c - 8) * 11. Running this for all five letters gives me NMFWP but I know the answer is supposed to be NOVEL. I do not know what I'm doing wrong.
In order to decrypt and affine cipher given a and b you need to use Dk = a^-1(y-b) mod m where m depends in the cardinality of the alphabet you are currently using (English 26, Italian 21, ...), a^-1 = m-a and k = (a, b).
For instance, vczkh with a=7 and b=8 gets decrypted into nqlmh given a^-1 = m - a = 26 - 7 = 19
So for v, since v is at position 21 in the english alphabet:
v -> 19(21-8) mod 26 -> 247 mod 26 -> 13 that corresponds to n
Here's a Javascript script I wrote
//Getting Args from console
var args = {
"operation" : process.argv[2],
"a" : parseInt(process.argv[3]),
"b" : parseInt(process.argv[4]),
"word" : process.argv[5]
};
var encryptedWord = [];
var decryptedWord = [];
if(!args.operation || !args.a || !args.b || !args.word){
console.log("Arguments are missing, please, use: node \"encrypt/decrypt\" a b word");
return;
} else {
if(typeof args.a === 'number' || typeof args.b === 'number'){
if(typeof args.word !== 'string'){
console.log("Word must be a string");
return;
} else {
// If a and m are coprimes
if(gcdCalc(args.a, 26) === 1){
if(args.operation === "encrypt"){
encryptWord().then(function(encrWord){
console.log("Word "+args.word+" got encrypted into "+encrWord);
});
} else if(args.operation === "decrypt"){
decryptWord().then(function(decrWord){
console.log("Ciphetext "+args.word+" got decrypted into "+decrWord);
});
} else {
console.log("Invalid operation specified. Use encrypt or decrypt.");
return;
}
} else {
console.log("a "+args.a+ " and m 26 are not coprimes");
return;
}
}
} else {
console.log("You must assign an Integer number to a and b. Remember that a must be coprime with m (26)");
return;
}
}
function gcdCalc(a, b) {
if (b) {
return gcdCalc(b, a % b);
} else {
return Math.abs(a);
}
};
function encryptWord(){
return new Promise( function(resolve){
var chars = args.word.split("");
var currInt = 0;
var currEnc = "";
chars.forEach( function( currChar){
currInt = parseInt(currChar, 36) - 10;
// E(a,b)(n) = an + b mod 26
currEnc = mod((args.a * currInt + args.b), 26);
encryptedWord.push(String.fromCharCode(97 + currEnc));
});
return resolve(encryptedWord.join(""));
});
}
function decryptWord(){
return new Promise( function(resolve){
var chars = args.word.split("");
var currInt = 0;
var currEnc = "";
//a^-1 = m - a
var a_1 = 26 - args.a;
chars.forEach( function( currChar){
currInt = parseInt(currChar, 36) - 10;
// D(y) = a^-1 * (y - b) mod 26
currEnc = mod((a_1 * (currInt - args.b)), 26);
decryptedWord.push(String.fromCharCode(97 + currEnc));
});
return resolve(decryptedWord.join(""));
});
}
function mod(n, m) {
var remain = n % m;
return Math.floor(remain >= 0 ? remain : remain + m);
};
To run it you need node, then, you can use it:
Encrypt : node affine-cipher.js encrypt 5 8 affine that becomes ihhwvc
Decrypt : node affine-cipher.js decrypt 5 8 ihhwvc that becomes affine
Notice that a and m MUST be coprime. For instance gcd(a, m) MUST be 1.
The total number of possible keys is 312 since a may vary in 12 different numbers which are coprime with 26 and b may vary in all 26 different number, so 12*26=312.
Hope I've been helpful.

Radius of projected sphere in screen space

I'm trying to find the visible size of a sphere in pixels, after projection to screen space. The sphere is centered at the origin with the camera looking right at it. Thus the projected sphere should be a perfect circle in two dimensions. I am aware of this 1 existing question. However, the formula given there doesn't seem to produce the result I want. It is too small by a few percent. I assume this is because it is not correctly taking perspective into account. After projecting to screen space you do not see half the sphere but significantly less, due to perspective foreshortening (you see just a cap of the sphere instead of the full hemisphere 2).
How can I derive an exact 2D bounding circle?
Indeed, with a perspective projection you need to compute the height of the sphere "horizon" from the eye / center of the camera (this "horizon" is determined by rays from the eye tangent to the sphere).
Notations:
d: distance between the eye and the center of the sphere
r: radius of the sphere
l: distance between the eye and a point on the sphere "horizon", l = sqrt(d^2 - r^2)
h: height / radius of the sphere "horizon"
theta: (half-)angle of the "horizon" cone from the eye
phi: complementary angle of theta
h / l = cos(phi)
but:
r / d = cos(phi)
so, in the end:
h = l * r / d = sqrt(d^2 - r^2) * r / d
Then once you have h, simply apply the standard formula (the one from the question you linked) to get the projected radius pr in the normalized viewport:
pr = cot(fovy / 2) * h / z
with z the distance from the eye to the plane of the sphere "horizon":
z = l * cos(theta) = sqrt(d^2 - r^2) * h / r
so:
pr = cot(fovy / 2) * r / sqrt(d^2 - r^2)
And finally, multiply pr by height / 2 to get the actual screen radius in pixels.
What follows is a small demo done with three.js. The sphere distance, radius and the vertical field of view of the camera can be changed by using respectively the n / f, m / p and s / w pairs of keys. A yellow line segment rendered in screen-space shows the result of the computation of the radius of the sphere in screen-space. This computation is done in the function computeProjectedRadius().
projected-sphere.js:
"use strict";
function computeProjectedRadius(fovy, d, r) {
var fov;
fov = fovy / 2 * Math.PI / 180.0;
//return 1.0 / Math.tan(fov) * r / d; // Wrong
return 1.0 / Math.tan(fov) * r / Math.sqrt(d * d - r * r); // Right
}
function Demo() {
this.width = 0;
this.height = 0;
this.scene = null;
this.mesh = null;
this.camera = null;
this.screenLine = null;
this.screenScene = null;
this.screenCamera = null;
this.renderer = null;
this.fovy = 60.0;
this.d = 10.0;
this.r = 1.0;
this.pr = computeProjectedRadius(this.fovy, this.d, this.r);
}
Demo.prototype.init = function() {
var aspect;
var light;
var container;
this.width = window.innerWidth;
this.height = window.innerHeight;
// World scene
aspect = this.width / this.height;
this.camera = new THREE.PerspectiveCamera(this.fovy, aspect, 0.1, 100.0);
this.scene = new THREE.Scene();
this.scene.add(THREE.AmbientLight(0x1F1F1F));
light = new THREE.DirectionalLight(0xFFFFFF);
light.position.set(1.0, 1.0, 1.0).normalize();
this.scene.add(light);
// Screen scene
this.screenCamera = new THREE.OrthographicCamera(-aspect, aspect,
-1.0, 1.0,
0.1, 100.0);
this.screenScene = new THREE.Scene();
this.updateScenes();
this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setSize(this.width, this.height);
this.renderer.domElement.style.position = "relative";
this.renderer.autoClear = false;
container = document.createElement('div');
container.appendChild(this.renderer.domElement);
document.body.appendChild(container);
}
Demo.prototype.render = function() {
this.renderer.clear();
this.renderer.setViewport(0, 0, this.width, this.height);
this.renderer.render(this.scene, this.camera);
this.renderer.render(this.screenScene, this.screenCamera);
}
Demo.prototype.updateScenes = function() {
var geometry;
this.camera.fov = this.fovy;
this.camera.updateProjectionMatrix();
if (this.mesh) {
this.scene.remove(this.mesh);
}
this.mesh = new THREE.Mesh(
new THREE.SphereGeometry(this.r, 16, 16),
new THREE.MeshLambertMaterial({
color: 0xFF0000
})
);
this.mesh.position.z = -this.d;
this.scene.add(this.mesh);
this.pr = computeProjectedRadius(this.fovy, this.d, this.r);
if (this.screenLine) {
this.screenScene.remove(this.screenLine);
}
geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0.0, 0.0, -1.0));
geometry.vertices.push(new THREE.Vector3(0.0, -this.pr, -1.0));
this.screenLine = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({
color: 0xFFFF00
})
);
this.screenScene = new THREE.Scene();
this.screenScene.add(this.screenLine);
}
Demo.prototype.onKeyDown = function(event) {
console.log(event.keyCode)
switch (event.keyCode) {
case 78: // 'n'
this.d /= 1.1;
this.updateScenes();
break;
case 70: // 'f'
this.d *= 1.1;
this.updateScenes();
break;
case 77: // 'm'
this.r /= 1.1;
this.updateScenes();
break;
case 80: // 'p'
this.r *= 1.1;
this.updateScenes();
break;
case 83: // 's'
this.fovy /= 1.1;
this.updateScenes();
break;
case 87: // 'w'
this.fovy *= 1.1;
this.updateScenes();
break;
}
}
Demo.prototype.onResize = function(event) {
var aspect;
this.width = window.innerWidth;
this.height = window.innerHeight;
this.renderer.setSize(this.width, this.height);
aspect = this.width / this.height;
this.camera.aspect = aspect;
this.camera.updateProjectionMatrix();
this.screenCamera.left = -aspect;
this.screenCamera.right = aspect;
this.screenCamera.updateProjectionMatrix();
}
function onLoad() {
var demo;
demo = new Demo();
demo.init();
function animationLoop() {
demo.render();
window.requestAnimationFrame(animationLoop);
}
function onResizeHandler(event) {
demo.onResize(event);
}
function onKeyDownHandler(event) {
demo.onKeyDown(event);
}
window.addEventListener('resize', onResizeHandler, false);
window.addEventListener('keydown', onKeyDownHandler, false);
window.requestAnimationFrame(animationLoop);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Projected sphere</title>
<style>
body {
background-color: #000000;
}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r61/three.min.js"></script>
<script src="projected-sphere.js"></script>
</head>
<body onLoad="onLoad()">
<div id="container"></div>
</body>
</html>
Let the sphere have radius r and be seen at a distance d from the observer. The projection plane is at distance f from the observer.
The sphere is seen under the half angle asin(r/d), so the apparent radius is f.tan(asin(r/d)), which can be written as f . r / sqrt(d^2 - r^2). [The wrong formula being f . r / d.]
The illustrated accepted answer above is excellent, but I needed a solution without knowing the field of view, just a matrix to transform between world and screen space, so I had to adapt the solution.
Reusing some variable names from the other answer, calculate the start point of the spherical cap (the point where line h meets line d):
capOffset = cos(asin(l / d)) * r
capCenter = sphereCenter + ( sphereNormal * capOffset )
where capCenter and sphereCenter are points in world space, and sphereNormal is a normalized vector pointing along d, from the sphere center towards the camera.
Transform the point to screen space:
capCenter2 = matrix.transform(capCenter)
Add 1 (or any amount) to the x pixel coordinate:
capCenter2.x += 1
Transform it back to world space:
capCenter2 = matrix.inverse().transform(capCenter2)
Measure the distance between the original and new points in world space, and divide into the amount you added to get a scale factor:
scaleFactor = 1 / capCenter.distance(capCenter2)
Multiply that scale factor by the cap radius h to get the visible screen radius in pixels:
screenRadius = h * scaleFactor

Resources