Rotate a polygon in here maps api - javascript

I am making a navigation program around a building, i am trying to rotate the polygons around the top right point as the origin. I have tried multiple equations, but nothing seems to work.
this is the code to display the polygons, i have commented out the code that rotates the polygons.
const scaleX = 0.000000114939702;
const scaleY = 0.000000152939702;
const originX = 53.4724642;
const originY = -2.2393615;
//usage:
readTextFile("result.json", function(text){
const data = JSON.parse(text);
for (const i in data) {
const list = [];
list.push(data[i].C1, data[i].C2, data[i].C3, data[i].C4);
for (const j in list) {
list[j][0] = (originX-list[j][0]*scaleX)
list[j][1] = (-(list[j][1]*scaleY)+originY)
}
var width = list[0][0] - list[1][0];
var height = list[0][1] - list[3][1];
var distanceToOriginX = originX-list[0][0]
var distanceToOriginY = originY-list[0][1]
//list[0][0] = originX - Math.sin(0.524)*distanceToOriginX+height
//list[0][1] = originY + Math.cos(0.524)*distanceToOriginY+width
addPolygonToMap(map, (list[0][0]), (list[0][1]), (list[1][0]), (list[1][1]), (list[2][0]), (list[2][1]), (list[3][0]), (list[3][1]), 'rgba(0, 0, 255, 1)')
}
}); ```

This is my solution, i had to re-calculate the scale and the calculations had to be done before the co-ordinates were converted into longitude and latitude.
const scaleX = 0.000000107;
const scaleY = 0.000000171;
const originX = 53.4724642;
const originY = -2.2393615;
function readFile(filename, floor, colour) {
readTextFile(filename, function(text){
const data = JSON.parse(text);
for (const i in data) {
const list = [];
list.push(data[i].C1, data[i].C2, data[i].C3, data[i].C4);
var angle = 0.484
for (const j in list) {
var distanceToOrigin = pythagorean(list[j][1], list[j][0])
var ang = 1.5708-(angle + Math.atan(list[j][0]/list[j][1]))
list[j][0] = originX-(Math.cos(ang)*distanceToOrigin)*scaleX
list[j][1] = originY-(Math.sin(ang)*distanceToOrigin)*scaleY
}
addPolygonToMap(map, (list[0][0]), (list[0][1]), (list[1][0]), (list[1][1]), (list[2][0]), (list[2][1]), (list[3][0]), (list[3][1]), colour, floor)
}
});

Related

Raycast not hitting a scene child using Three.js with GMaps

Im trying to hit a pin scene child with a click in GMaps, using Three.js and threejs-overlay-view but I can't get the object clicked. Raycaster.intersectObjects only returns [].
Image of pin
Here's a snippet of the code, everything works fine except the intersection part.
(async () => {
const map = await initMap();
const mapDiv = map.getDiv();
const mousePosition = new Vector2();
const raycaster = new THREE.Raycaster();
const overlay = new ThreeJSOverlayView(mapOptions.center);
const scene = overlay.getScene();
overlay.setMap(map);
...
map.addListener('click', ev => {
const {domEvent} = ev;
const {left, top, width, height} = mapDiv.getBoundingClientRect();
const x = domEvent.clientX - left;
const y = domEvent.clientY - top;
mousePosition.x = 2 * (x / width) - 1;
mousePosition.y = 1 - 2 * (y / height);
// dont get nothing here...
raycaster.setFromCamera(mousePosition, overlay.camera);
const intersects = raycaster.intersectObjects(scene.children, true);
overlay.requestRedraw();
});
overlay.update = () => {
// ... and here
raycaster.setFromCamera(mousePosition, overlay.camera);
const intersects = raycaster.intersectObjects(scene.children, true);
...
overlay.requestRedraw();
};
})();
Glad if any help, Thanks!

An algorithm for animating a point over a closed path like square or triangle in canvas?

How to animate a point over a closed path. For the example below, I could pixel push this but I'm wondering if there is a better math(y) solution for animating over a path.
CodeSandbox
Note: I'm using a ctx.drawImg() to represent the point because I'd like the point to rotate along the path (image below code).
import "./styles.scss";
import o from "./letters/0.svg";
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const arena = 500;
const margin = 60;
const edge = arena - margin;
let data = [o];
var imgEls = [];
function drawArena() {
ctx.beginPath();
ctx.lineWidth = "3";
ctx.strokeStyle = "DeepSkyBlue";
const origin = [margin, margin];
ctx.moveTo(...origin);
ctx.lineTo(edge, margin);
ctx.lineTo(edge, edge);
ctx.lineTo(margin, edge);
ctx.lineTo(...origin);
ctx.stroke();
}
function loadImages() {
return data.map((item, i) => {
return new Promise((res, rej) => {
let img = new Image();
let svg = new Blob([item], { type: "image/svg+xml" });
let url = window.URL.createObjectURL(svg);
img.onload = () => {
imgEls.push(img);
window.URL.revokeObjectURL(url);
console.log("loaded");
res(img);
};
img.src = url;
});
});
}
Promise.all(loadImages()).then(() => {
// window.requestAnimationFrame(draw);
draw();
});
function draw(t) {
// window.requestAnimationFrame(draw);
drawArena();
imgEls.forEach((img, index) => {
const offset = index * img.width;
const x = margin + offset;
const y = margin - img.height;
ctx.drawImage(img, x, y);
});
}
If you're just tracking between 2 points; You're looking for Linear Interpolation.
Here's what it is:
In mathematics, linear interpolation is a method of curve fitting using linear polynomials to construct new data points within the range of a discrete set of known data points.
a demo in P5js
and a simple implementation in JS
function lerp(v0, v1, t) {
return v0*(1-t)+v1*t
}
via https://github.com/mattdesl/lerp/blob/master/index.js
Tracking over a complex path is decidedly more complex. Let me know if you need that functionality and I'll see what I can dig up.

How do I draw a line to point derived from angle?

I'm writing a component (React) which draws a fancy box around a bit of text with SVG. The component receives the width and height of the dom element and draws a polygon derived from those values. Simplified example follows:
import React from 'react';
const Box = ({ dimensions }) => {
const { width, height } = dimensions;
const mod = 10;
const tlX = 0;
const tlY = 0;
const trX = tlX + width;
const trY = tlY;
const brX = trX;
const brY = trY + height;
const blX = 0;
const blY = tlY + height;
return (
<picture>
<svg height={height + 50} width={width + 200}>
<polygon
points={`${tlX},${tlY} ${trX},${trY} ${brX},${brY} ${blX},${blY}`}
style={{ fill: 'black', fillOpacity: '0.5' }}
/>
</svg>
</picture>
);
};
In this stripped down example the result is a rectangle with straight corners based on the width and height of the supplied dom element. In reality these values are given some random modifiers to create more of a trapezoid, as in fig A.
Illustration of desired result
In the fig B you can see my problem with this method. When drawing a longer or shorter box, the figure looks squished. What I want is for the box to behave like in fig C, in that it will draw the horizontal lines at a given angle until it has reached a certain width.
From what I can intuit this should be possible with some math savvy, but I am unable to quite figuring it out on my own.
Thanks in advance for any input, and please let me know if I'm being unclear on anything.
Edit:
A "trapezoid" shape is apparently not what I'm looking for. My apologies. I just want a sort of janky rectangle. I was asked to show the code I've been using in more detail. As you will see I am basically just taking the values from the last example and messing them up a bit by adding or subtracting semi-randomly.
import React from 'react';
import PropTypes from 'prop-types';
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
const point = (core, edge) => getRandomArbitrary(core - edge, core + edge);
const Box = ({ dimensions }) => {
const { width, height } = dimensions;
const mod = 10;
const tlX = point(25, mod);
const tlY = point(40, mod);
const trX = point(width + 55, mod);
const trY = point(25, mod);
const brX = point(width + 25, mod);
const brY = point(height - 25, mod);
const blX = point(5, mod);
const blY = point(height - 40, mod);
return (
<picture>
<svg height={height + 50} width={width + 200}>
<polygon
points={`${tlX},${tlY} ${trX},${trY} ${brX},${brY} ${blX},${blY}`}
style={{ fill: 'black', fillOpacity: '0.5' }}
/>
</svg>
</picture>
);
};
Box.propTypes = {
dimensions: PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number,
}).isRequired,
};
export default Box;
In order to calculate the y for the top right point I'm imagining a circle with the center in the top left point. The radius of the circle is tlX - trX, The angle is -5 degs but you can change it to what you need. In order to calculate the value for the y you can do
const trY = tlY - (tlX - trX)*Math.sin(a1)
To calculate the y for the bottom right point I'm doing the same only this time the angle is 5 degs, the center in the bottom left point and the radius of the circle is blX - brX
const brY = blY - (blX - brX)*Math.sin(a2)
The important part of the demo is this:
//calculating the points for the polygon
const tlX = BB.x-10;
const tlY = BB.y-5;
const trX = tlX + 20 + BB.width;
//const trY = tlY - 10;
const trY = tlY - (tlX - trX)*Math.sin(a1)
const brX = trX - 5;
const blX = tlX + 5;
const blY = tlY + 10 + BB.height;
//const brY = trY + 30+ BB.height;
const brY = blY - (blX - brX)*Math.sin(a2)
Next comes a demo where I'm using plain javascript. Please change the length of the text to see if this is what you need.
let bb = txt.getBBox();
let m = 10;
// the blue rect
updateSVGelmt({x:bb.x-m,y:bb.y-m,width:bb.width+2*m,height:bb.height+2*m},theRect)
// the bounding box of the blue rect
let BB = theRect.getBBox();
//the angles for the polygon
let a1 = -5*Math.PI/180;
let a2 = -a1;
//calculating the points for the polygon
const tlX = BB.x-10;
const tlY = BB.y-5;
const trX = tlX + 20 + BB.width;
//const trY = tlY - 10;
const trY = tlY - (tlX - trX)*Math.sin(a1)
const brX = trX - 5;
const blX = tlX + 5;
const blY = tlY + 10 + BB.height;
//const brY = trY + 30+ BB.height;
const brY = blY - (blX - brX)*Math.sin(a2)
let points = `${tlX},${tlY} ${trX},${trY} ${brX},${brY} ${blX},${blY}`;
poly.setAttributeNS(null, "points", points)
let polybox = poly.getBBox();
svg.setAttributeNS(null, "viewBox", `${polybox.x-2} ${polybox.y-2} ${polybox.width+4} ${polybox.height+4}`)
svg.setAttributeNS(null, "width",3*(polybox.width+4))
function updateSVGelmt(o,elmt) {
for (let name in o) {
if (o.hasOwnProperty(name)) {
elmt.setAttributeNS(null, name, o[name]);
}
}
}
svg{border:1px solid}
<svg id="svg">
<polygon id="poly"
points=""
style="stroke: black; fill:none"
/>
<rect id="theRect" fill="#d9d9ff" />
<text id="txt" text-anchor="middle">element</text>
</svg>

Given min-value, max-value and mean, can I elegantly generate data to fit a bell curve?

Let's say I have an array of objects.
I have 3 values associated with this array: min-height, max-height and average-height.
I want to assign a height to each object so that:
No object's height is less than min-height
No object's height is greater than max-height
The mean of all objects' heights is average-height.
Essentially I am looking to generate a height distribution like this:
The heights have to be pseudo-random - that is to say, I want to be able to get a height for each object by feeding the result of a random number generator into a function and getting the height returned.
My solution at the moment is to split my range of acceptable heights (all between min-height and max-height) into a series of bins and assign a probability to each bin. Once a bin is selected, I choose a height from within that range at random.
This is not an ideal solution as it is inelegant, clunky, and produces a stepped curve as opposed to a smooth one.
Here is my current code for producing the bins:
var min_height = 10
var max_height = 100
var avg_height = 30
var scale = SCALE ()
.map_from([min_height, avg_height, max_height])
.map_to([-Math.PI, 0, Math.PI])
var range = max_height - min_height;
var num_of_bins = 10
var bin_size = range/num_of_bins;
var bins = []
var sum_of_probability = 0
while (bins.length < num_of_bins) {
var bin = {};
bin.min = min_height + (bins.length*bin_size);
bin.max = bin.min + bin_size;
bin.mid = bin.min + (bin_size/2);
bin.probability = Math.cos(scale(bin.mid))+1
sum_of_probability += bin.probability;
bins.push(bin)
}
var i;
var l = bins.length;
for (i=0; i<l; i++) {
bins[i].probability /= sum_of_probability
if (bins[i-1]) {
bins[i].cumulative_probability = bins[i-1].cumulative_probability + bins[i].probability;
}
else {
bins[i].cumulative_probability = bins[i].probability;
}
}
Essentially I would love to be able to generate pseudo-random data to roughly fit a curve in an elegant way, and I am not sure if this is possible in javascript. Let me know if you think this is do-able.
I borrowed the Gaussian "class" from here: html5 draw gaussian function using bezierCurveTo.
The stuff that's really relevant to you is the getPoints() function. Basically, given a min, max and average height, getPoints() will return an array with a smooth gaussian curve of values. You can then take those points and scale them over whatever range you would need (just multiply them).
The numSteps value of generateValues (which getPoints has hard-coded to 1000) controls how many values you get back, giving you a better "resolution". If you did something like 10, you'd have the values for something like your bar graph. Given 1000 gives a nice smooth curve.
Hope this helps.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 200;
var Gaussian = function(mean, std) {
this.mean = mean;
this.std = std;
this.a = 1/Math.sqrt(2*Math.PI);
};
Gaussian.prototype = {
addStd: function(v) {
this.std += v;
},
get: function(x) {
var f = this.a / this.std;
var p = -1/2;
var c = (x-this.mean)/this.std;
c *= c;
p *= c;
return f * Math.pow(Math.E, p);
},
generateValues: function(start, end, numSteps = 100) {
var LUT = [];
var step = (Math.abs(start)+Math.abs(end)) / numSteps;
for(var i=start; i<end; i+=step) {
LUT.push(this.get(i));
}
return LUT;
}
};
const getPoints = () => {
const minHeight = 0;
const maxHeight = 200;
const averageHeight = 50;
const start = -10;
const end = 10;
const mean = averageHeight / (maxHeight - minHeight) * (end - start) + start;
const std = 1;
const g = new Gaussian(mean, std);
return g.generateValues(start, end, 1000);
}
const draw = () => {
const points = getPoints();
// x-axis
ctx.moveTo(0, canvas.height - 20);
ctx.lineTo(canvas.width, canvas.height - 20);
// y-axis
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.moveTo(0, canvas.height - 20);
console.log(points);
for (let i = 0; i < points.length; i++) {
ctx.lineTo(i * (canvas.width / points.length),
canvas.height - points[i] * canvas.height - 20);
}
ctx.stroke();
};
draw();
body {
background: #000;
}
canvas {
background: #FFF;
}
<canvas></canvas>

Updating canvas values in javascript

I have a particle animation written in JavaScript using Canvas.
What i'm trying to do is to change the canvas drawing values, specifically radMax and radMin. I have written the code here for you to see: https://jsfiddle.net/u3wwxg58/
What happens now, is that when I call function f(), new particles are added with the right radMax and radMin values, instead of updating the current "drawing" with the new radMax and radMin values. Basically, what i'm trying to do is to simply make my sphere / animation larger when function f() is called.
My code for drawParticle()
var cvs = document.createElement('canvas'),
context = cvs.getContext('2d');
document.body.appendChild(cvs);
var numDots = 500,
n = numDots,
currDot,
maxRad = 100,
minRad = 90,
radDiff = maxRad-minRad,
dots = [],
PI = Math.PI,
centerPt = {x:0, y:0};
resizeHandler();
window.onresize = resizeHandler;
while(n--){
currDot = {};
currDot.radius = minRad+Math.random()*radDiff;
currDot.radiusV = 10+Math.random()*50,
currDot.radiusVS = (1-Math.random()*2)*0.005,
currDot.radiusVP = Math.random()*PI,
currDot.ang = (1-Math.random()*2)*PI;
currDot.speed = 0;
//currDot.speed = 1-Math.round(Math.random())*2;
//currDot.speed = 1;
currDot.intensityP = Math.random()*PI;
currDot.intensityS = Math.random()*0.5;
currDot.intensityO = 64+Math.round(Math.random()*64);
currDot.intensityV = Math.min(Math.random()*255, currDot.intensityO);
currDot.intensity = Math.round(Math.random()*255);
currDot.fillColor = 'rgb('+currDot.intensity+','+currDot.intensity+','+currDot.intensity+')';
dots.push(currDot);
}
function drawPoints(){
var n = numDots;
var _centerPt = centerPt,
_context = context,
dX = 0,
dY = 0;
_context.clearRect(0, 0, cvs.width, cvs.height);
var radDiff,currDot;
//draw dots
while(n--) {
currDot = dots[n];
currDot.radiusVP += currDot.radiusVS;
radDiff = currDot.radius+Math.sin(currDot.radiusVP)*currDot.radiusV;
dX = _centerPt.x+Math.sin(currDot.ang)*radDiff;
dY = _centerPt.y+Math.cos(currDot.ang)*radDiff;
//currDot.ang += currDot.speed;
currDot.ang += currDot.speed*radDiff/40000;
currDot.intensityP += currDot.intensityS;
currDot.intensity = Math.round(currDot.intensityO+Math.sin(currDot.intensityP)*currDot.intensityV);
//console.log(currDot);
_context.fillStyle= 'rgb('+currDot.intensity+','+currDot.intensity+','+currDot.intensity+')';
_context.fillRect(dX, dY, 1, 1);
console.log('draw dots');
} //draw dot
window.requestAnimationFrame(drawPoints);
}
function resizeHandler(){
var box = cvs.getBoundingClientRect();
var w = box.width;
var h = box.height;
cvs.width = w;
cvs.height = h;
centerPt.x = Math.round(w/2);
centerPt.y = Math.round(h/2);
}
drawPoints();
and my code for updating the values:
var myi = 0, timex = 20;
function f() {
numDots =500+myi*10; maxRad = 300;minRad = 200 ; n=numDots;
while(n--){
currDot = {};
currDot.radius = minRad+Math.random()*radDiff;
currDot.radiusV = 10+Math.random()*500,
currDot.radiusVS = (1-Math.random()*2)*0.005,
currDot.radiusVP = Math.random()*PI,
currDot.ang = (1-Math.random()*2)*PI;
currDot.speed = (1-Math.random()*2);
//currDot.speed = 1-Math.round(Math.random())*2;
//currDot.speed = 1;
currDot.intensityP = Math.random()*PI;
currDot.intensityS = Math.random()*0.05;
currDot.intensityO = 64+Math.round(Math.random()*64);
currDot.intensityV = Math.min(Math.random()*255, currDot.intensityO);
currDot.intensity = Math.round(Math.random()*255);
currDot.fillColor = 'rgb('+currDot.intensity+','+currDot.intensity+','+currDot.intensity+')';
dots.push(currDot);
//setTimeout(function(){n++},1000);
}
myi++;
if( myi < timex ){
setTimeout( f, 500 );
}}
f();
Picture to show what I want to do: https://postimg.org/image/9uhb3jda9/
So left one is before calling function f(), right one is when f() is called.
Function f is adding dots because the statement currDot = {}; creates a new object, and the statement dots.push(currDot);
adds it to the array of dots.
If you change it to:
currDot = dots[n];
and remove the push then it will act on the existing dots.
However, that will only work while myi is zero.
Presumably you are intending to increase the number of dots over time. Perhaps what you really want is just to completely replace the existing dots?
In which case just stick dots = []; before the while loop and leave the rest as-is.
No point iterating all the particles again just to change the size of the effect. Do it while you are rendering the particles.
From your code add the variable radiusGrowAt and increase each dot radius every time you render it. radiusGrowAt assumes the frame rate is constant and at 60fps
//just after document.body.appendChild(cvs) where you declare and define
maxRad = 20,
minRad = 10,
radDiff = maxRad-minRad,
//=================================================================
radiusGrowAt = 20 / 60, //<<== add this // grow 20 pixels every 60 frames (one second)
//=================================================================
dots = [],
PI = Math.PI,
centerPt = {x:0, y:0};
... etc
Then
//draw dots
while(n--) {
currDot = dots[n];
//=================================================================
currDot.radius += radiusGrowAt; //<<== add this line
//=================================================================
currDot.radiusVP += currDot.radiusVS;
radDiff = currDot.radius+Math.sin(currDot.radiusVP)*currDot.radiusV;
... etc

Categories

Resources