I have a working raphael.js fiddle : http://jsfiddle.net/El4a/sbxjfafx/10/
Now, I need the created svg inside my angular-meteor project. I need it on several places so it has to be abstract. So first question already: should I use a factory or a directive for this?
I've been trying to make it a factory for now, but to be honest, I have no idea what I'm doing. I don't have any errors, but the image doesn't show up.
This is the raphael code inside a factory :
'use strict';
angular.module('timeAppApp').factory('svgFactory', function($rootScope){
Raphael.fn.pieChart = function (cx, cy, r, values) {
var paper = this,
rad = Math.PI / 180,
chart = this.set();
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
var bcolors = ['#d667cd','#3D8C1E','#00b9ff'];
var colors = ['FF5DF4','#69F233', '#0080B0'];
var angle = 0,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2),
// color = "hsb(" + start + ", 1, .5)",
color = colors[j%colors.length],
ms = 500,
delta = 30,
//bcolor = "hsb(" + start + ", 1, 1)",
bcolor = bcolors[j%bcolors.length],
p = sector(cx, cy, r, angle, angle + angleplus, {gradient: "100-" + bcolor + "-" + color}),
txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad), cy + (r + delta + 25) * Math.sin(-popangle * rad));
angle += angleplus;
chart.push(p);
chart.push(txt);
start += .1;
};
for (var i = 0, ii = values.length; i < ii; i++) {
total += values[i];
}
for (var i = 0; i < ii; i++) {
process(i);
}
return chart;
};
Raphael.fn.circle = function(cx, cy, r){
var paper = this,
rad = Math.PI / 180,
chart = this.set();
var circle = paper.circle(280, 180, 175);
circle.attr("fill", "white");
circle.attr("stroke", "#fff");
return chart;
};
var r;
var svg = function (raphael) {
$(function () {
var values = [20, 60, 20];
r = raphael("svg", 700, 700);
r.pieChart(350, 350, 100, values, "#fff");
r.circle(350, 350, 85).attr({ fill: 'white' });
})
};
svg(raphael);
return{r : r};
When I debug in the browser, it shows that all the raphael functions get skipped. I havent a clue as to why.
The console says : "ReferenceError: raphael is not defined",
but when I watch Raphael I get a running version :
"Raphael: Your browser supports SVG. You are running Raphaƫl 2.1.2"
So I'm trying to get the div with id "svg", and draw the image inside that. This does not happen and the div remains empty.
I have injected the new factory
angular.module('timeAppApp')
.controller('ProjectDetailController', function($scope, $meteor, $state, $stateParams, svgFactory)
and try to draw the image in the matching view. <div id="svg"></div>
I know I'm probably doing 127things wrong and raphael/angular enthousiast are crying blood, but I really don't know what :(
As an added plus, I also kinda need to be able to pass 3 values to the factory, instead of having them hardcoded.
Thanks in advance for drudging through this mess!
I'm not sure how, but it all worked out. I ditched the factory idea, and used a directive. This worked without any issues.
Related
I am trying to create a random terrain in THREE JS using the plane geometry and randomizing the pos array using this method:
let side = 200;
const geometry = new PlaneGeometry(80, 20, side, 100);
// const material = new MeshStandardMaterial({ color: '#271033'});
let material = new THREE.MeshStandardMaterial({
roughness: 1,
color: new THREE.Color('#271033'),
flatShading: true,
});
const plane = new Mesh(geometry, material);
plane.rotation.x = - Math.PI / 2 - Math.PI / 20;
plane.position.y = - 4;
plane.position.z = - 5;
plane.castShadow = true;
plane.receiveShadow = true;
let pos = geometry.getAttribute("position");
let pa = pos.array as any;
const hVerts = geometry.parameters.heightSegments + 1;
const wVerts = geometry.parameters.widthSegments + 1;
let noise2D = createNoise2D();
for (let j = 0; j < hVerts; j++) {
for (let i = 0; i < wVerts; i++) {
const ex = 1.1;
pa[3 * (j * wVerts + i) + 2] =
( noise2D(i / 100, j / 100) + noise2D((i + 100) / 50, j / 50) * Math.pow(ex, 0) );
}
}
When I try to sample points on the mesh(so that I can place tree's or whatever at said point) the points don't seem to be valid points on the Mesh. I believe it may be return points from a plane that didn't receive the rotation/position transform, but I'm not sure.
Here is the code for the sampling:
const plane = createPlane();
plane1Sampler = new MeshSurfaceSampler(plane).build();
// add plane to scene etc...
for ( let i = 0; i < 10; i ++ ) {
const tree = await loadTree();
const _position = new THREE.Vector3();
const _normal = new THREE.Vector3();
plane1Sampler.sample( _position, _normal );
tree.scale.set(0.1, 0.1, 0.1);
tree.position.set(_position.x, _position.y, _position.z);
this.scene.add( tree );
}
Finally, here's a picture of the result, the tree's should be positioned on the first lighter purple plane. I'm not really sure what the issue here is so any help is very much appreciated! Also, the project is in React and TS.
For whatever reason the sampler was indeed not sampling from a plane with the translations applied. By applying the same translation to the points I was able to get valid points.
// translation applied to the plane, which for some reason the sampler doesn't take into account
_position.applyAxisAngle(new Vector3(1, 0, 0), - Math.PI / 2 - Math.PI / 20);
_position.y -= 6.1;
_position.z -= 5;
When running this script on p5 js's sandbox it's working really fine
When trying to use it inside react i get : Expected an assignment or function call and instead saw an expression no-unused-expressions
I checked previous questions and found out that most of the problems are syntax ones due to not using brackets or semicolons . I did all the good syntax practices but didn't get to make it function
Here is my sketch code on react :
export default function sketch(p){
var t;
var canvas ;
var w = window.innerWidth;
var h = window.innerHeight;
var x , y , d , T;
p.setup=()=>{
// put setup code here
t=0;
}
p.draw = () => {
canvas =p.createCanvas(w-50, h-50);
canvas.style('display','block');
var x_center = (p.windowWidth - w) / 2;
var y_center = (p.windowHeight - h) / 2;
canvas.position(x_center, y_center);
t += .01;
for (var j = 2; x = y = j--;){
for (var i = w; d = p.noise(t - i / 99) * 3, i--; x += Math.cos(d) * 6, y += Math.sin(d) * 6){
j ? i - w / 2 || ( T = p.translate)(-x, -y) : p.push() + T(x + w / 2, y + w / 2) + p.rotate(d + Math.PI / 4) + p.line(-600, 100, 600, 100) + p.pop(i % 100 || p.scale(80));
}
}
}
window.onresize = function() {
// assigns new values for width and height variables
w = window.innerWidth;
h = window.innerHeight;
canvas.size(w,h);
}
}
Write out p.translate(x, y) every time rather than using T = p.translate and then T(x, y), e.g.,
export default function sketch(p){
let t;
let canvas ;
let w = window.innerWidth;
let h = window.innerHeight;
let x ; let y ; let d ;
p.setup=()=>{
canvas =p.createCanvas(w-50, h-50);
canvas.style('display','block');
// put setup code here
t=0;
const x_center = (p.windowWidth - w) / 2;
const y_center = (p.windowHeight - h) / 2;
canvas.position(x_center, y_center);
}
p.draw = () => {
p.background(255);
t += .0008;
for (let j = 2; x = y = j--;){ // j >0
for (let i = w-200; d = p.noise(t - i / 99) * 3, i--; x += Math.cos(d) * 6, y += Math.sin(d) * 6, i>0){ // i >0
j ? i - w / 2 || p.translate(-x, -y) : p.push() + p.translate(x + w / 2, y + w / 2) + p.rotate(d + Math.PI / 4) + p.line(-600, 100, 600, 100) + p.pop(i % 100 || p.scale(80));
}
}
}
}
Your initial version does not work because in instance mode translate() is a method belonging to p, and thus wants to access other methods or fields of p. The function T that you defined along the way, however, does not belong to p and thus does not have access to any this.foos.
I am using react-konva for drawing shapes on a stage and to edit them I select that shape and the shape has be set to focus on stage(i.e come to the center of the stage).
On the click of a button I am updating the X,Y and scale of the stage but I am not getting the shape exactly at the center of the stage.
Please see this demo and share your inputs on this:
https://codesandbox.io/s/shapecenterdemo-o9hg2?file=/src/ShapeCenterDemo.jsx
PS: Can't remove the dragBoundFunc from the stage.
Based on your demo:
focusRect = () => {
let { rect, stageWidth, stageHeight } = this.state;
let newScale = 6;
const rectWidth = 50 * newScale;
const rectHeight = 50 * newScale;
const x = - rect.x * newScale + stageWidth / 2 - rectWidth / 2 ;
const y = - rect.y * newScale + stageHeight / 2 - rectHeight / 2;
let pos = this.boundFunc({ x, y }, newScale);
this.setState({
stageX: pos.x,
stageY: pos.y,
stageScale: newScale
});
};
https://codesandbox.io/s/react-konva-center-a-shape-7ttts
I have multiple firebase items that I am looping through and they have name, categoryId, lat, lon.
I am trying to calculate a distance from 2 lat,lon (one is the one in firebase), the other is the user's location.
All of this is fine, and I can calculate it fine too. However, how do I inject/map the new variable into the firebase array that I am subscribed to?
this.categoryId = this.navParams.get('categoryId');
afoDatabase.list('/list', {query: {
orderByChild: "categoryId",
equalTo: parseInt(this.categoryId)
}}).subscribe(listItems => {
this.items = listItems; //need to add distance within array
loadingPopup.dismiss().catch(() => {});
});
For example, I have a variable called distance, that calculates everything and has the value. How do I add it in the this?items - so I can call it from the HTML side?
Thanks for the help :)
To inject, first create a function and create a variable (e.g. this.distance) and get the value of your function on this variable. After that use a for loop to increment, based on length - and use this.items[i]["distance"] to add to each one. For example:
this.categoryId = this.navParams.get('categoryId');
afoDatabase.list('/list', {query: {
orderByChild: "categoryId",
equalTo: parseInt(this.categoryId)
}}).subscribe(listItems => {
this.items = listItems;
this.geolocation.getCurrentPosition({timeout:15000}).then((resp) => {
this.myLat = resp.coords.latitude;
this.myLong = resp.coords.longitude;
}).catch((error) => {
console.log('Error getting location', error);
});
for (var i = 0, len = this.items.length; i < len; i++) {
this.distance = this.calculateDistance(this.myLat, this.myLong, this.items[i].lat, this.items[i].lng);
this.items[i]["distance"] = Math.round(this.distance);
console.log('testing myLat', this.myLat)
console.log('testing myLong', this.myLong)
}
console.log('testing inject', this.items)
loadingPopup.dismiss().catch(() => {});
});
Function
calculateDistance(lat1:number,lon1:number,lat2:number,lon2:number){
let R = 6371; // km
let dLat = (lat2 - lat1) * Math.PI / 180;
let dLon = (lon2 - lon1) * Math.PI / 180;
let a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
let d = R * c;
return d;
}
I'm writing a Leaflet plugin that extends the polyline functionality. In the my plugin I'm accessing the path segments using the SVGPathSegList interface. But according to the Chrome DevTools the interface will be removed in Chrome 48. I'm seeking for another possibility to access the the path segments.
Here's my fiddle.
(function () {
var __onAdd = L.Polyline.prototype.onAdd,
__onRemove = L.Polyline.prototype.onRemove,
__updatePath = L.Polyline.prototype._updatePath,
__bringToFront = L.Polyline.prototype.bringToFront;
L.Polyline.include({
onAdd: function (map) {
__onAdd.call(this, map);
this._textRedraw();
},
onRemove: function (map) {
__onRemove.call(this, map);
},
bringToFront: function () {
__bringToFront.call(this);
this._textRedraw();
},
_textRedraw: function () {
var textNodes = this._path.parentElement.getElementsByTagName('text'),
tnIndex;
if (textNodes.length > 0) {
for (tnIndex = textNodes.length - 1; tnIndex >= 0; tnIndex -= 1) {
textNodes[tnIndex].parentNode.removeChild(textNodes[tnIndex]);
}
}
if (this.options.measurements) {
this.setText();
}
},
setText: function () {
var path = this._path,
points = this.getLatLngs(),
pathSeg,
prevPathSeg,
center,
angle,
rotation,
textNode;
/*
* If not in SVG mode or Polyline not added to map yet return
* setText will be called by onAdd, using value stored in this._text
*/
if (!L.Browser.svg || typeof this._map === 'undefined') {
return this;
}
for (pathSeg = 0; pathSeg < path.pathSegList.length; pathSeg += 1) {
if (pathSeg > 0) {
prevPathSeg = path.pathSegList[pathSeg - 1];
center = this._calcCenter(
prevPathSeg.x,
prevPathSeg.y,
path.pathSegList[pathSeg].x,
path.pathSegList[pathSeg].y
);
angle = this._calcAngle(
prevPathSeg.x,
prevPathSeg.y,
path.pathSegList[pathSeg].x,
path.pathSegList[pathSeg].y
);
rotation = 'rotate(' + angle + ' ' +
center.x + ',' + center.y + ')';
debugger;
textNode = document
.createElementNS('http://www.w3.org/2000/svg', 'text');
textNode.setAttribute('text-anchor', 'middle');
textNode.setAttribute('x', center.x);
textNode.setAttribute('y', center.y);
textNode.setAttribute('transform', rotation);
textNode.textContent = points[pathSeg - 1]
.distanceTo(points[pathSeg]);
this._path.parentElement.appendChild(textNode);
} else {
continue;
}
}
},
_calcCenter: function (x1, y1, x2, y2) {
return {
x: (x1 + x2) / 2,
y: (y1 + y2) / 2
}
},
_calcAngle: function (x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
},
_updatePath: function () {
__updatePath.call(this);
this._textRedraw();
}
});
})();
#holger-will gave some very useful links.
You can modify your js code to use the new API.
for example:
Use var pathData = path.getPathData() instead of old var segs = path.pathSegList;
Use pathData[1].values[4] instead of old path.pathSegList.getItem(1).x
Use path.setPathData(pathData) to update the path element instead of old path.pathSegList.appendItem/insertItem/removeItem
Include the path-data-polyfill.js for browsers which have not supported the new API.
(Chrome 50 still has not implemented getPathData and setPathData. There may be a long way...)
Here is a code sample:
//svg code:
//...
//<path d="M0,0 L100,50" id="mypath"></path>
//<script href="/js/path-data-polyfill.js"></script>
//...
//js code:
var path = document.getElementById('mypath');
var pathdata = path.getPathData();
console.log(pathdata);
console.log(pathdata.length); //2
console.log(pathdata[0].type); //"M"
console.log(pathdata[0].values); //[0,0]
console.log(pathdata[1].type); //"L"
console.log(pathdata[1].values); //[100,50]
pathdata.push({type: "C", values: [100,-50,200,150,200,50]}); //add path segment
path.setPathData(pathdata); //set new path data
console.log(path.getAttribute('d')); //"M0,0 L100,50 C100,-50,200,150,200,50"
path data polyfill: https://github.com/jarek-foksa/path-data-polyfill