Paper.js animating : Movement according to a paths normal - paperjs

I am trying to animate a line and its normal ( another line ). But when i change position after or before setting rotation of the normal strange animation occurs.
Is there anybody who has an idea on that?
I have this code in sketchpad:
http://sketch.paperjs.org/#S/jVJLb5tAEP4rKy7BqgvUbS+OcogstT2klaUeeohzIDA2q+AZtAyOI8v/vbM7mFDLUcqJne8xz0OE+RaiefT7CbioomlUUOnfu9wZ6hjcD3NjZll2vcIh9EdCn4fQxlHXSATh2Xz3//GkR9rGIvTIMucqPuzn2dS8zLOjp6x4xYGS5GU5YJo0/XKZ8lELeCWe0Vp29AQLqslJ4isH5VWPa0m4HNcTtIjLc9lj3YHXCeLzBj5Z5FgqzCaTC8BXRYJfmgrc2B2xz7VMHqnDsk2YmjtYc6BoQWFy3mhR2bp0gPF96GIqqgfvpYSGWsuWUNxkArIL373M/6hWqJ3VVAhBp7ABvqMi96Jbjj/NstNGkNw2r8e8XyHyyuoHMsopxvKUJnUgjjhniNUpyXFTg+p2Fp4Twm9ODkpk6w4L7xDDDpAn5rBCM/r6GXC4E4tdK5KfspNEHipJ2IrRab3KLOgfrjwvc9PULCqpDQxXYPZmaIfWIdLCZisyc+pLVf0JKdbezx607+TFfLgZUr/L3nv2GXfcw7uLGo/pP5c2JDnp3l7h2D1c6lsLFcdjdPwL
var outerH = 200;
var outerW = 300;
var group = new Group();
var spine = new Path({x:0, y:0});
spine.add({x:0, y:outerH/4});
spine.add({x:-outerW, y:outerH});
spine.strokeColor = 'red';
var nP = new Path();
nP.strokeColor = 'blue';
nP.add(new Point(0, 0))
nP.add(new Point(50, 0));
//nP.pivot = nP.bounds.topLeft;
group.addChildren([spine, nP]);
group.position = {x:200, y:300};
var loc = spine.getLocationAt(120);
var normal = spine.getNormalAt(120);
nP.position = loc.point;
nP.rotate(normal.angle);
view.onFrame = function(event) {
var sinus = Math.sin(event.time );
var cosinus = Math.cos(event.time );
// Change the x position of the segment point;
spine.segments[2].point.y += cosinus ;
spine.segments[2].point.x += sinus ;
var loc = spine.getLocationAt(120);
var normal = spine.getNormalAt(120);
nP.position = loc.point;
//nP.rotate(normal.angle);
}
If I uncomment -> nP.rotate(normal.angle); nP is not rotating with the line normal point?

Please read the following post on the mailing list that explains this behavior and offers an option to switch paper.js into a mode that simplifies this scenario:
https://groups.google.com/forum/#!searchin/paperjs/applymatrix/paperjs/4EIRSGzcaUI/seKoNT-PSpwJ

The problem is that in paper.js when you rotate an item and move it, the new rotation is accepted as 0 degree. So I am keeping an oldAngle variable outside the onFrame event. Than do the necessary math in onFrame to calculate the right rotation...
The right code:
var group = new Group();
var spine = new Path({x:0, y:0});
spine.add({x:0, y:50});
spine.add({x:-300, y:200});
spine.strokeColor = 'red';
var nP = new Path();
nP.strokeColor = 'blue';
nP.add(new Point(0, 0))
nP.add(new Point(50, 0));
group.addChildren([spine, nP]);
group.position = {x:200, y:300};
var loc = spine.getLocationAt(120);
var normal = spine.getNormalAt(120);
nP.position = loc.point;
nP.rotation = normal.angle;
var oldAngle = normal.angle; // keep the old rotation angle in this variable
view.onFrame = function(event) {
var sinus = Math.sin(event.time );
spine.segments[2].point.y += sinus ;
spine.segments[2].point.x += sinus ;
var loc = spine.getLocationAt(120);
var normal = spine.getNormalAt(120);
nP.position = loc.point;
nP.rotation += normal.angle - oldAngle; // here we do the necessary math
oldAngle = normal.angle;
}

Related

Group an Array of Vectors according to certain Floors in Three.js

I have a list of Vectors which represent points on three different floors in three.js.
I am trying to group these vectors according to the floor they belong to. Is there a good formula to do this? Perhaps find height from on vector or something. Not sure how to go about this. Any help would be appreciated.
Thanks,
Rob
As Wilt said... Can't really help you without more info.
Still, if your floors are even and all stand on the xz plane (in my example), You may indeed check the points' height (position.y) against the floors'.
var container, renderer, scene, camera, controls;
var floors = [];
var points = [], materials = [], heights = [];
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
container = document.createElement('div');
document.body.appendChild(container);
container.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// camera + controls
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 50, 750);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.autoRotate = true;
//floors
for(i=0; i<3; i++) {
var planeGeometry = new THREE.BoxGeometry(500, 500, 10, 10);
var planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
transparent: true,
opacity : 0.3,
depthWrite : false //get rid of coplanar glitches wall/floor
});
materials.push(planeMaterial);
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = Math.PI / 2;
//plane.rotation.y = Math.PI / 8; //Uncomment to see this doesn't work if the floors move, i.e. changing rotation/position. If this is what you need, just raycast from point to floor in the animation loop and count how many floors the ray goes through (intersects.length)
plane.position.y = 75*i;
heights.push(plane.position.y);
floors.push(plane);
scene.add(plane);
}
//wall
var height = heights[2];
var planeGeometry = new THREE.BoxGeometry(500, height+100, 10, 10);
var planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
transparent: true,
opacity:0.3,
depthWrite : false //get rid of coplanar glitches wall/floor
});
materials.push(planeMaterial);
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.y = heights[1]+45;
plane.position.z = -510/2;
scene.add(plane);
// points
for (i=0; i<200; i++) {
var sphereGeometry = new THREE.SphereGeometry(3, 32, 16);
var sphere = new THREE.Mesh(sphereGeometry);
sphere.position.x = Math.random() * 500 - 250;
sphere.position.y = Math.random() * 300 - 100;
sphere.position.z = Math.random() * 500 - 250;
scene.add(sphere);
points.push(sphere);
}
// events
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function addSpheres() {
//addSpheres
for (i=0;i<200;i++) {
var that = points[i].position.y;
points[i].position.y = ( that < heights[0] ) ? 200 : that - 0.5;
if ( that > heights[0] && that < heights[1] ) points[i].material = materials[0];
if ( that < heights[2] && that > heights[1] ) points[i].material = materials[1];
if ( that > heights[2] ) points[i].material = materials[2];
points[i].material.needsUpdate = true;
}
}
function animate() {
controls.update();
addSpheres();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
Now, feel free to "group" these points according to your needs.
Please note that "vectors" are different from "points". Read more about the difference.
Raycasting would be the way to go if you had a more complex scene (moving floors/different planes, points moving in different directions).

Conditional Line Graph using Open Flash Charts

I am using Open Flash Charts v2. I have been trying to make Conditional line graph. But I couldn't find any straight forward way, example or any class for producing Conditional charts.
Example of Conditional Graph
So I thought to use some techniques to emulate conditional graph ,I made separate Line object for values above limit range and then this line is used to overlap the plotted line.
This techniques works some what ok ,but there are problems with it,
How to color or place the conditional colored line exactly above the limit.
Remove tooltip and dot from limit line.
Tooltip of conditional line(red) and plotted line(green) are both shown ,I only need tooltip of green line.
Conditional Line Graph Problem illustrated
Source Code: // C#
var chart = new OpenFlashChart.OpenFlashChart();
var data1 = new List<double?> { 1, 3, 4, 5, 2, 1, 6, 7 };//>4=
var overlap = new List<double?> { null, null, 4, 5, null, null, null, null };
var overlap2 = new List<double?> { null, null, null, null, null, null, 6, 7 };
var limitData = new List<double?> { 4, 4, 4, 4, 4, 4, 4, 4 };
var line1 = new Line();
line1.Values = data1;
//line1.HaloSize = 0;
line1.Width = 2;
line1.DotSize = 5;
line1.DotStyleType.Tip = "#x_label#<br>#val#";
line1.Colour = "#37c855";
line1.Tooltip = "#val#";
var overLine = new Line();
overLine.Values = overlap;
//overLine.HaloSize = 0;
overLine.Width = 2;
overLine.DotSize = 5;
overLine.DotStyleType.Tip = "#x_label#<br>#val#";
overLine.Colour = "#d81417";
overLine.Tooltip = "#val#";
var overLine2 = new Line();
overLine2.Values = overlap2;
//overLine2.HaloSize = 0;
overLine2.Width = 2;
overLine2.DotSize = 5;
//overLine2.DotStyleType.Tip = "#x_label#<br>#val#";
//overLine2.DotStyleType.Type = DotType.DOT;
overLine2.Colour = "#d81417";
overLine2.Tooltip = "#val#";
var limit = new Line();
limit.Values = limitData;
limit.Width = 2;
limit.Colour = "#ff0000";
limit.HaloSize = -1;
limit.DotSize = -1;
// limit.DotStyleType.Tip = "";
limit.DotStyleType.Type = null;
//limit.Tooltip = "";
chart.AddElement(line1);
chart.AddElement(overLine);
chart.AddElement(overLine2);
chart.AddElement(limit);
chart.Y_Legend = new Legend("Experiment");
chart.Title = new Title("Conditional Line Graph");
chart.Y_Axis.SetRange(0, 10);
chart.X_Axis.Labels.Color = "#e43456";
chart.X_Axis.Steps = 4;
chart.Tooltip = new ToolTip("#val#");
chart.Tooltip.Shadow = true;
chart.Tooltip.Colour = "#e43456";
chart.Tooltip.MouseStyle = ToolTipStyle.CLOSEST;
Response.Clear();
Response.CacheControl = "no-cache";
Response.Write(chart.ToPrettyString());
Response.End();
Note:
I have already downloaded the OFC (Open Flash Charts) source ,If I modify the OFC Line.as source than how would I be able to generate json for the changed graph ? ,b/c I'm currently using .Net library for the json generation for OFC charts,please do let me know this also.
Update:
I have modified the source code on the advice of David Mears I'm using FlashDevelop for ActionScript.
P.S: I'm open for ideas if another library can do this job.
If you don't mind a little rebuilding, you can get the source of OFC here and modify the Line.solid_line() method in open-flash-chart/charts/Line.as to do this fairly easily.
In order to set the extra chart details through JSON using the .NET library, you'll also have to modify OpenFlashChart/LineBase.cs to add alternative colour and boundary properties. I'm not hugely familiar with .NET, but based on the existing properties you might add something like this:
private double boundary;
private string altcolour;
[JsonProperty("boundary")]
public virtual double Boundary
{
set { this.boundary = value; }
get { return this.boundary; }
}
[JsonProperty("alt-colour")]
public virtual string AltColour
{
set { this.altcolour = value; }
get { return this.altcolour; }
}
Then I believe the following should work in Line.as:
public function solid_line(): void {
var first:Boolean = true;
var i:Number;
var tmp:Sprite;
var x:Number;
var y:Number;
var last_e:Element;
var ratio:Number;
for ( i=0; i < this.numChildren; i++ ) {
// Step through every child object.
tmp = this.getChildAt(i) as Sprite;
// Only include data Elements, ignoring extra children such as line masks.
if( tmp is Element )
{
var e:Element = tmp as Element;
if( first )
{
if (this.props.get('alt-colour') != Number.NEGATIVE_INFINITY) {
if (e._y >= this.props.get_colour('boundary'))
{
// Line starts below boundary, set alt line colour.
this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('alt-colour') );
}
else
{
// Line starts above boundary, set normal line colour.
this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('colour') );
}
}
// Move to the first point.
this.graphics.moveTo(e.x, e.y);
x = e.x;
y = e.y;
first = false;
}
else
{
if (this.props.get('alt-colour') != Number.NEGATIVE_INFINITY) {
if (last_e._y < this.props.get_colour('boundary') && e._y >= this.props.get_colour('boundary'))
{
// Line passes below boundary. Draw first section and switch to alt colour.
ratio = (this.props.get_colour('boundary') - last_e._y) / (e._y - last_e._y);
this.graphics.lineTo(last_e.x + (e.x - last_e.x) * ratio, last_e.y + (e.y - last_e.y) * ratio);
this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('alt-colour') );
}
else if (last_e._y >= this.props.get_colour('boundary') && e._y < this.props.get_colour('boundary'))
{
// Line passes above boundary. Draw first section and switch to normal colour.
ratio = (this.props.get_colour('boundary') - last_e._y) / (e._y - last_e._y);
this.graphics.lineTo(last_e.x + (e.x - last_e.x) * ratio, last_e.y + (e.y - last_e.y) * ratio);
this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('colour') );
}
}
// Draw a line to the next point.
this.graphics.lineTo(e.x, e.y);
}
last_e = e;
}
}
if ( this.props.get('loop') ) {
// close the line loop (radar charts)
this.graphics.lineTo(x, y);
}
}
With the new open-flash-chart.swf, you should be able to just set your new properties on line1:
line1.Boundary = 4;
line1.AltColour = "#d81417";

Drawing a polygon around a polyline in google maps

I am trying to create a polygon around an existing polyline. I thought of drawing polylines parallel to the existing polyline and then joining them to create a polygon. I tried unsuccessfully to do the math to draw the parallel lines. I found this link which I used to create the polylines on either sides.
http://wtp2.appspot.com/ParallelLines.htm
It seemed exactly what I was looking for. I started the conversion from v2 to v3. I tried to keep to minimal code and deleted the rest. I have also removed the listener for change in zoom level which was present in the original code.
It worked perfectly when I used a small fixed polyline. However when I increased the size of the polyline the parallel polylines began to go haywire.
The code that I have is :
var points = null;
var map;
var line1;
var line2;
var prj = null;
var idlelistener;
var gapPx = 2;
var weight = 4;
function BDCCParallelLines(maps, point, bounds) {
map = maps;
points = point;
//map.fitBounds(bounds);
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(map);
// Wait for idle map
idlelistener = google.maps.event.addListener(map, 'idle', function() {
// Get projection
prj = overlay.getProjection();
recalc();
})
}
function recalc() {
google.maps.event.removeListener(idlelistener);
var zoom = this.map.getZoom();
//left and right swapped throughout!
var pts1 = new google.maps.MVCArray();//left side of center
var pts2 = new google.maps.MVCArray();//right side of center
//shift the pts array away from the centre-line by half the gap + half the line width
var o = (this.gapPx + this.weight)/2;
var p2l,p2r;
for (var i=1; i<this.points.length; i+=2){
var p1lm1;
var p1rm1;
var p2lm1;
var p2rm1;
var thetam1;
var p1 = this.prj.fromLatLngToContainerPixel(this.points.getAt(i-1),zoom) //**fromLatLngToPixel
var p2 = this.prj.fromLatLngToContainerPixel(this.points.getAt(i),zoom) //**fromLatLngToPixel
var theta = Math.atan2(p1.x-p2.x,p1.y-p2.y) + (Math.PI/2);
var dl = Math.sqrt(((p1.x-p2.x)*(p1.x-p2.x))+((p1.y-p2.y)*(p1.y-p2.y)));
if(theta > Math.PI)
theta -= Math.PI*2;
var dx = Math.round(o * Math.sin(theta));
var dy = Math.round(o * Math.cos(theta));
var p1l = new google.maps.Point(p1.x+dx,p1.y+dy); //GPoint
var p1r = new google.maps.Point(p1.x-dx,p1.y-dy);
p2l = new google.maps.Point(p2.x+dx,p2.y+dy);
p2r = new google.maps.Point(p2.x-dx,p2.y-dy);
if(i==1){ //first point
pts1.push(this.prj.fromContainerPixelToLatLng(p1l),zoom); //**fromPixelToLatLng
pts2.push(this.prj.fromContainerPixelToLatLng(p1r),zoom); //**fromPixelToLatLng
}
else{ // mid points
if(theta == thetam1){
// adjacent segments in a straight line
pts1.push(this.prj.fromContainerPixelToLatLng(p1l),zoom);
pts2.push(this.prj.fromContainerPixelToLatLng(p1r),zoom);
}
else{
var pli = this.intersect(p1lm1,p2lm1,p1l,p2l);
var pri = this.intersect(p1rm1,p2rm1,p1r,p2r);
var dlxi = (pli.x-p1.x);
var dlyi = (pli.y-p1.y);
var drxi = (pri.x-p1.x);
var dryi = (pri.y-p1.y);
var di = Math.sqrt((drxi*drxi)+(dryi*dryi));
var s = o / di;
var dTheta = theta - thetam1;
if(dTheta < (Math.PI*2))
dTheta += Math.PI*2;
if(dTheta > (Math.PI*2))
dTheta -= Math.PI*2;
if(dTheta < Math.PI){
//intersect point on outside bend
pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1),zoom);
pts1.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*dlxi),p1.y+(s*dlyi))),zoom);
pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
}
else if (di < dl){
pts1.push(this.prj.fromContainerPixelToLatLng(pli),zoom);
}
else{
pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1),zoom);
pts1.push(this.prj.fromContainerPixelToLatLng(p1l),zoom);
}
dxi = (pri.x-p1.x)*(pri.x-p1.x);
dyi = (pri.y-p1.y)*(pri.y-p1.y);
if(dTheta > Math.PI){
//intersect point on outside bend
pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1),zoom);
pts2.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*drxi),p1.y+(s*dryi))),zoom);
pts2.push(this.prj.fromContainerPixelToLatLng(p1r),zoom);
}
else if(di<dl)
pts2.push(this.prj.fromContainerPixelToLatLng(pri),zoom);
else{
pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1),zoom);
pts2.push(this.prj.fromContainerPixelToLatLng(p1r),zoom);
}
}
}
p1lm1 = p1l;
p1rm1 = p1r;
p2lm1 = p2l;
p2rm1 = p2r;
thetam1 = theta;
}
pts1.push(this.prj.fromContainerPixelToLatLng(p2l),zoom);//final point
pts2.push(this.prj.fromContainerPixelToLatLng(p2r),zoom);
this.line1 = new google.maps.Polyline({
map: map,
path: pts1,
strokeColor: "#0000FF",
strokeWeight: 4,
strokeOpacity: 1.0
});
this.line2 = new google.maps.Polyline({
map: map,
path: pts2,
strokeColor: "#0000FF",
strokeWeight: 4,
strokeOpacity: 1.0
});*/
createPolygon(pts1,pts2);
}
function intersect(p0,p1,p2,p3)
{
// this function computes the intersection of the sent lines p0-p1 and p2-p3
// and returns the intersection point,
var a1,b1,c1, // constants of linear equations
a2,b2,c2,
det_inv, // the inverse of the determinant of the coefficient matrix
m1,m2; // the slopes of each line
var x0 = p0.x;
var y0 = p0.y;
var x1 = p1.x;
var y1 = p1.y;
var x2 = p2.x;
var y2 = p2.y;
var x3 = p3.x;
var y3 = p3.y;
// compute slopes, note the cludge for infinity, however, this will
// be close enough
if ((x1-x0)!=0)
m1 = (y1-y0)/(x1-x0);
else
m1 = 1e+10; // close enough to infinity
if ((x3-x2)!=0)
m2 = (y3-y2)/(x3-x2);
else
m2 = 1e+10; // close enough to infinity
// compute constants
a1 = m1;
a2 = m2;
b1 = -1;
b2 = -1;
c1 = (y0-m1*x0);
c2 = (y2-m2*x2);
// compute the inverse of the determinate
det_inv = 1/(a1*b2 - a2*b1);
// use Kramers rule to compute xi and yi
var xi=((b1*c2 - b2*c1)*det_inv);
var yi=((a2*c1 - a1*c2)*det_inv);
return new google.maps.Point(Math.round(xi),Math.round(yi)); // ** CHANGED HERE
}
function createPolygon(side1,side2){
var a = new Array();
for(var i = 0; i < side1.length;i++){
a.push(side1.getAt(i))
}
for(var i = side1.length-1; i >=0;i--){
a.push(side2.getAt(i));
}
drawPolylinePolygon(a)
}
function drawPolylinePolygon(a){
a.push(a[0]);
var color = getColor(false);
var polygon_options = {
paths: a,
strokeColor: color,
strokeOpacity: 0.7,
strokeWeight: 2,
fillColor: color,
fillOpacity: 0.2
};
current_polygon = new google.maps.Polygon(polygon_options);
current_polygon.setMap(map);
}
The createPolygon() function is used to merge the two polylines to create a polygon.
This is the html page :
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title></title>
<script src="http://maps.google.com/maps/api/js?sensor=true&libraries=drawing,geometry" type="text/javascript"></script>
<script src="BDCCParallelLines.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
var map;
function linesMap(){
var latlng1 = new google.maps.LatLng(51.42, -0.95);
var mapOptions = {zoom: 22, center:latlng1, mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeControl: false};
var map = new google.maps.Map(document.getElementById('mapLines'),mapOptions);
var pts = new Array();
var latlngbounds = new google.maps.LatLngBounds();
pts.push (new google.maps.LatLng(51.42, -0.97));
latlngbounds.extend(new google.maps.LatLng(51.42, -0.97));
pts.push (new google.maps.LatLng(51.43, -0.96));
latlngbounds.extend(new google.maps.LatLng(51.43, -0.96));
pts.push (new google.maps.LatLng(51.425, -0.955));
latlngbounds.extend(new google.maps.LatLng(51.425, -0.955));
pts.push (new google.maps.LatLng(51.42, -0.95));//straight at zoom = 13
latlngbounds.extend(new google.maps.LatLng(51.42, -0.95));
pts.push (new google.maps.LatLng(51.43, -0.94));
latlngbounds.extend(new google.maps.LatLng(51.43, -0.94));
pts.push (new google.maps.LatLng(51.43, -0.9375));//horz & straight
latlngbounds.extend(new google.maps.LatLng(51.43, -0.9375));
pts.push (new google.maps.LatLng(51.43, -0.935));
latlngbounds.extend(new google.maps.LatLng(51.43, -0.935));
pts.push (new google.maps.LatLng(51.425, -0.935));
latlngbounds.extend(new google.maps.LatLng(51.425, -0.935));
pts.push (new google.maps.LatLng(51.42, -0.935));//vert & straight
latlngbounds.extend(new google.maps.LatLng(51.42, -0.935));
var poly = new BDCCParallelLines(map,pts,latlngbounds);
var poly2 = new google.maps.Polyline({
map: map,
path: pts,
strokeColor: "#FF0000",
strokeWeight: 2,
strokeOpacity: 1.0
});
}
//]]>
</script>
</head>
<body onload="linesMap();"
style="font-weight: bold; font-size: large; font-family: Arial; background-color: #cccc99">
<div id="mapLines" style="width: 800px; height: 600px">
</div>
</body>
</html>
After searching I came across this article where Ben seems to have the same problem. The image on the link shows the exact same problem I'm having.
Google maps api parallel path lines
I would like to know if there is any way that I can improve on the existing code for the parallel polylines or if there is any other way the end result I am looking for is a polygon around the polyline.
You should use a Buffer-function that exists in any spatial-API or database, one example are
sharpmap.

How to put a infowindow on polyline in Google Maps v3?

I want to know how to show infowindow on polyline in using Google Maps Api V3? and to appear in the middle of the polyline ?!
Firstly you will need to calculate the middle/center of the polyline. This has been discussed and answered here;
https://stackoverflow.com/a/9090409/787921
Then you will have to open the infowindow;
var infowindow = new google.maps.InfoWindow({
content: "infowindow text content"});
infowindow.setPosition(midLatLng);
infowindow.open(map);
find the middle point and set your custom view .
func showPath(polyStr :String){
polyline?.map = nil
mapView1.reloadInputViews()
pathDraw = GMSPath(fromEncodedPath: polyStr)!
polyline = GMSPolyline(path: pathDraw)
polyline?.strokeWidth = 4.0
polyline?.strokeColor = UIColor.init(red: 247/255.0, green: 55/255.0, blue: 76/255.0, alpha: 1.0)
polyline?.map = mapView1
let poinsCount = pathDraw.count()
let midpoint = pathDraw.coordinate(at: poinsCount)
DispatchQueue.main.async
{
self.addMarkerPin(corrdinate: midCordinate, distance: "10 min")
}
}
func addMarkerPin(corrdinate:CLLocationCoordinate2D, distance: String)
{
let marker = GMSMarker()
marker.position = corrdinate
PathTimeView = PathInfoView.loadFromNib() //here i am load Xib file, you can use your custom view
let DynamicView=PathTimeView
DynamicView.timelbl.text = distance
UIGraphicsBeginImageContextWithOptions(DynamicView.frame.size, false, UIScreen.main.scale)
DynamicView.layer.render(in: UIGraphicsGetCurrentContext()!)
let imageConverted: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
marker.icon = imageConverted
marker.map = self.mapView1
marker.infoWindowAnchor = CGPoint(x: -1900 , y: -2000)
}
First you should got center/middle of polyline and this what works for me
private fun centerPos(points: MutableList<LatLng>): LatLng {
val middleDistance = SphericalUtil.computeLength(points).div(2)
return extrapolate(points, points.first(), middleDistance.toFloat()) ?: points[0]
}
private fun extrapolate(path: List<LatLng>, origin: LatLng, distance: Float): LatLng? {
var extrapolated: LatLng? = null
if (!PolyUtil.isLocationOnPath(
origin,
path,
false,
1.0
)
) { // If the location is not on path non geodesic, 1 meter tolerance
return null
}
var accDistance = 0f
var foundStart = false
val segment: MutableList<LatLng> = ArrayList()
for (i in 0 until path.size - 1) {
val segmentStart = path[i]
val segmentEnd = path[i + 1]
segment.clear()
segment.add(segmentStart)
segment.add(segmentEnd)
var currentDistance = 0.0
if (!foundStart) {
if (PolyUtil.isLocationOnPath(origin, segment, false, 1.0)) {
foundStart = true
currentDistance = SphericalUtil.computeDistanceBetween(origin, segmentEnd)
if (currentDistance > distance) {
val heading = SphericalUtil.computeHeading(origin, segmentEnd)
extrapolated = SphericalUtil.computeOffset(
origin,
(distance - accDistance).toDouble(),
heading
)
break
}
}
} else {
currentDistance = SphericalUtil.computeDistanceBetween(segmentStart, segmentEnd)
if (currentDistance + accDistance > distance) {
val heading = SphericalUtil.computeHeading(segmentStart, segmentEnd)
extrapolated = SphericalUtil.computeOffset(
segmentStart,
(distance - accDistance).toDouble(),
heading
)
break
}
}
accDistance += currentDistance.toFloat()
}
return extrapolated
}
then You can add infoWindow with normal way with your platform at it is differ from each platform

mapping rect in small image to larger image (in order to do a copyPixels operation)

this is (I think) a relatively simple math question but I've spent a day banging my head against it and have only the dents and no solution...
I'm coding in actionscript 3 - the functionality is:
large image loaded at runtime. The bitmapData is stored and a smaller version is created to display on the available screen area (I may end up just scaling the large image since it is in memory anyway).
The user can create a rectangle hotspot on the smaller image (the functionality will be more complex: multiple rects with transparency: example a donut shape with hole, etc)
3 When the user clicks on the hotspot, the rect of the hotspot is mapped to the larger image and a new bitmap "callout" is created, using the larger bitmap data. The reason for this is so the "callout" will be better quality than just scaling up the area of the hotspot.
The image below shows where I am at so far- the blue rect is the clicked hotspot. In the upper left is the "callout" - copied from the larger image. I have the aspect ratio right but I am not mapping to the larger image correctly.
Ugly code below... Sorry this post is so long - I just figured I ought to provide as much info as possible. Thanks for any tips!
--trace of my data values
*source BitmapDada 1152 864
scaled to rect 800 600
scaled BitmapData 800 600
selection BitmapData 58 56
scaled selection 83 80
ratio 1.44
before (x=544, y=237, w=58, h=56)
(x=544, y=237, w=225.04, h=217.28)
*
Image here: http://i795.photobucket.com/albums/yy237/skinnyTOD/exampleST.jpg
public function onExpandCallout(event:MouseEvent):void{
if (maskBitmapData.getPixel32(event.localX, event.localY) != 0){
var maskClone:BitmapData = maskBitmapData.clone();
//amount to scale callout - this will vary/can be changed by user
var scale:Number =150 //scale percentage
var normalizedScale :Number = scale/=100;
var w:Number = maskBitmapData.width*normalizedScale;
var h:Number = maskBitmapData.height*normalizedScale;
var ratio:Number = (sourceBD.width /targetRect.width);
//creat bmpd of the scaled size to copy source into
var scaledBitmapData:BitmapData = new BitmapData(maskBitmapData.width * ratio, maskBitmapData.height * ratio, true, 0xFFFFFFFF);
trace("source BitmapDada " + sourceBD.width, sourceBD.height);
trace("scaled to rect " + targetRect.width, targetRect.height);
trace("scaled BitmapData", bkgnImageSprite.width, bkgnImageSprite.height);
trace("selection BitmapData", maskBitmapData.width, maskBitmapData.height);
trace("scaled selection", scaledBitmapData.width, scaledBitmapData.height);
trace("ratio", ratio);
var scaledBitmap:Bitmap = new Bitmap(scaledBitmapData);
var scaleW:Number = sourceBD.width / scaledBitmapData.width;
var scaleH:Number = sourceBD.height / scaledBitmapData.height;
var scaleMatrix:Matrix = new Matrix();
scaleMatrix.scale(ratio,ratio);
var sRect:Rectangle = maskSprite.getBounds(bkgnImageSprite);
var sR:Rectangle = sRect.clone();
var ss:Sprite = new Sprite();
ss.graphics.lineStyle(8, 0x0000FF);
//ss.graphics.beginFill(0x000000, 1);
ss.graphics.drawRect(sRect.x, sRect.y, sRect.width, sRect.height);
//ss.graphics.endFill();
this.addChild(ss);
trace("before " + sRect);
w = uint(sRect.width * scaleW);
h = uint(sRect.height * scaleH);
sRect.inflate(maskBitmapData.width * ratio, maskBitmapData.height * ratio);
sRect.offset(maskBitmapData.width * ratio, maskBitmapData.height * ratio);
trace(sRect);
scaledBitmapData.copyPixels(sourceBD, sRect, new Point());
addChild(scaledBitmap);
scaledBitmap.x = offsetPt.x;
scaledBitmap.y = offsetPt.y;
}
}
Thanks!
public function onExpandCallout(event:MouseEvent):void{
// TODO: build this on startup or only on click? Speed vs memory
if (calloutState == true) return;
if (maskBitmapData.getPixel32(event.localX, event.localY) != 0){
calloutState = true;
//create bitmap from source using scaled selection rect
var ratio:Number = (sourceBMD.width /targetRect.width);
var sRect:Rectangle = hotSpotSprite.getBounds(bkgnImageSprite);
var destRect:Rectangle = new Rectangle(sRect.x * ratio, sRect.y * ratio, sRect.width * ratio, sRect.height * ratio);
calloutBitmapData = new BitmapData(destRect.width, destRect.height, true, 0xFFFFFFFF);
calloutBitmap = new Bitmap(calloutBitmapData);
//-- scale alpha mask
var scaledMaskBitmapData:BitmapData = new BitmapData(destRect.width, destRect.height, true, 0x00000000);
var maskScale:Number = scaledMaskBitmapData.width / maskBitmapData.width;
var mMatrix:Matrix = new Matrix(maskScale, 0, 0, maskScale);
scaledMaskBitmapData.draw(maskBitmapData,mMatrix,null,null,null, false);
// copy source with scaled alpha
calloutBitmapData.copyPixels(sourceBMD, destRect, new Point(), scaledMaskBitmapData, new Point());
scaledMaskBitmapData = null;
// apply filter to bitmap
var myDropShadowFilter:DropShadowFilter = new DropShadowFilter();
myDropShadowFilter.distance = 12;
myDropShadowFilter.alpha = .3
myDropShadowFilter.strength = 1;
myDropShadowFilter.blurX = 8;
myDropShadowFilter.blurY = 8;
calloutBitmap.filters = [myDropShadowFilter];
//place on screen
calloutSprite = new Sprite();
calloutSprite.addChild(calloutBitmap)
calloutSprite.x = offsetPt.x;
calloutSprite.y = offsetPt.y;
// ADD TO PARENT DisplayContainer
calloutLayer.addChild(calloutSprite);
// calloutSprite.scaleX = 2;
// calloutSprite.scaleY = 2;
calloutSprite.doubleClickEnabled = true;
calloutSprite.addEventListener(MouseEvent.DOUBLE_CLICK, onCollapseCallout);
calloutSprite.addEventListener(MouseEvent.MOUSE_DOWN, onStartDrag);
calloutSprite.addEventListener(MouseEvent.MOUSE_UP, onStopDrag);
}
}

Resources