I wish to do circle animation using d3. i wish to show circle growing on mouse hover. for this i have used following code. the code runs without error how it does not produce any circle animation. any advice where I am doing wrong. please see the code below
var vis = d3.select(e).append("svg")
.attr("width", svgwidth)
.attr("height", svgheight);
var circles = vis.selectAll("circle").data(sampleData);
var circleEnter=circles.enter().append("g").append("circle");
var circles = vis.selectAll("circle").data(sampleData);
var circleEnter=circles.enter().append("g").append("circle");
circleEnter.attr("cx", function (d,i)
{
Cxs[i]= xRange (d.x);
return xRange (d.x);
})
.attr("cy", function (d,i) {
Cys[i]= yRange (d.y); return yRange (d.y);
})
.attr("r", 20)
.style("fill-opacity", .2)
.style("fill", fillColor)
.on("mouseover", function(d,i)
{
d3.select(this).select("circle").transition()
.duration(750)
.attr("r",30);
}
.on("mouseout", function(d,i)
{
d3.select(this).select("circle").transition()
.duration(750)
.attr("r",20);
}
You need just d3.select(this) -- this will already select the circle element:
.on("mouseover", function(d,i) {
d3.select(this).transition()
.duration(750)
.attr("r",30);
}).on("mouseout", function(d,i) {
d3.select(this).transition()
.duration(750)
.attr("r",20);
})
Related
I'm new to SwiftUI and I'm still working on my spinning wheel. For now I wanna show people the results on screen by Text("(Array[index])"). the question is i know the random rotation degrees but i don't know how to transfer it to that "result angles" index :P ,Any help I will appreciate it !
enter image description here
I use Path to draw the wheel:
var slices: Array<OneChance.OneChanceData> {
let sum = Double(newChoices.count)
var endDeg: Double = 0.0
var tempSlices: [OneChance.OneChanceData] = []
for (color, text) in zip(colors, newChoices) {
let degrees: Double = Double(360 / sum)
tempSlices.append(OneChance.OneChanceData(startAngle: Angle(degrees: endDeg), endAngle: Angle(degrees: endDeg + degrees), text: text, color: color))
endDeg += degrees
}
return tempSlices
}
The wheel view like:
ForEach(0..<chanceVM.newChoices.count) { i in
EachSliceView(chanceData: self.chanceVM.slices[i])
}
I use onTapGesture to get random wheel spinning degrees:
.onTapGesture {
self.chanceVM.showResult = false
self.chanceVM.rotateDegree = 0.0
playSound(sound: "wheelSpinning13s", type: "mp3")
self.chanceVM.allowToTapAgain = false
withAnimation(Animation.easeOut(duration: 13.0)) {
self.chanceVM.rotateDegree = Double.random(in: 5400.0...7200.0)
}
print("\(chanceVM.rotateDegree)")
print("\((Int(chanceVM.rotateDegree) % 360))")
print("\((Int(chanceVM.rotateDegree) % 360)/(360/chanceVM.newChoices.count))")
self.chanceVM.delayText()
self.chanceVM.delayTap()
}
So how can i get the index after random spinning degrees?
I'm trying to activate collision detection while dragging an object. The behaviour I'm looking for can be seen with the second example (circles) seen here with another library:
library example
https://codepen.io/osublake/pen/bb6983d03e1c3582f9aac486ab9069f8
From my understanding, Phaser's collision engine will only work when object position is automatically updated through its velocity. Is it really the case ? If so, what would be a clean solution ?
My best solution right now is to change velocity in the update() method and use the pointer movement along with the delta time to calculate velocity. With constant deltas, the object movement would match the pointer movement. However, the result is a bit sketchy and has problems when the mouse stops to move (with button still down).
if (obj.membre.isDragging){
let prevPos = {x: obj.membre.x, y: obj.membre.y};
let pointer = scene.input.activePointer;
let velocityX = 0;
let velocityY = 0;
if (pointer.position.x !== status.pointerX && pointer.position.y !== status.pointerY){
status.pointerX = pointer.position.x;
status.pointerY = pointer.position.y;
velocityX = (pointer.position.x-pointer.prevPosition.x)/status.deltaS;
velocityY = (pointer.position.y-pointer.prevPosition.y)/status.deltaS;
}
obj.membre.setVelocity(velocityX, velocityY);
}
Matter engine would make it work, but here is an acceptable solution with arcade engine. The trick is to add LOTS of drag (as in friction). It also seams to work fine in the drag callback.
scene.input.on('drag', function (pointer, gameObject, dragX, dragY) {
gameObject.body.setAllowGravity(false);
gameobject.setDrag(1000,1000);
//set velocity to match pointer movement - status.deltaS is frame delta in seconds
velocityX = (pointer.position.x-pointer.prevPosition.x)/status.deltaS;
velocityY = (pointer.position.y-pointer.prevPosition.y)/status.deltaS;
gameObject.setVelocity(velocityX, velocityY);
});
Here is the solution a finally retained. It uses a custom speration function, based on the example http://labs.phaser.io/edit.html?src=src%5Cphysics%5Carcade%5Ccustom%20separate.js
collider in create()
this.physics.add.collider(obj.dynamicGroup,obj.dynamicGroup,customSeparate);
adjust body properties for all objects (necessary? based on example)
object.body.customSeparateX = true;
object.body.customSeparateY = true;
regular drag
scene.input.on('drag', function (pointer, gameObject, dragX, dragY) {
gameObject.x = dragX;
gameObject.y = dragY;
}
and here is the custom seperation function. I didn't know how to make it cleaner.
function customSeparate(s1, s2) {
var b1 = s1.body;
var b2 = s2.body;
//for dragged object, we have no velocity, so we take pointer direction
let pointFacingX = "left";
let pointFacingY = "top";
if (scene.input.activePointer.position.x > scene.input.activePointer.prevPosition.x) pointFacingX = "right";
if (scene.input.activePointer.position.y > scene.input.activePointer.prevPosition.y) pointFacingY = "bottom";
//if we have velocity we use that - could add priority to fastest object
if (b1.velocity.x > 0) pointFacingX = "right";
if (b2.velocity.x > 0) pointFacingX = "right";
if (b1.velocity.y > 0) pointFacingY = "bottom";
if (b2.velocity.y > 0) pointFacingY = "bottom";
let overlapX = 0;
let overlapY = 0;
if(b1.x > b2.x) {
overlapX = b2.right - b1.left;
}
else {
overlapX = b1.right - b2.left;
}
if(b1.y > b2.y) {
overlapY = b2.bottom - b1.top;
}
else {
overlapY = b1.bottom - b2.top;
}
//we move according to smallest overlap **no overlap is coded at 10000
if (overlapX <= 0) overlapX = 10000;
if (overlapY <= 0) overlapY = 10000;
if(overlapX < overlapY){
if (pointFacingX === "left"){
if (b1.x > b2.x) {
b2.x -= overlapX;
b2.stop();
}
else {
b1.x -= overlapX;
b1.stop();
}
}
else{
if (b1.x < b2.x) {
b2.x += overlapX;
b2.stop();
}
else {
b1.x += overlapX;
b1.stop();
}
}
}
else{
if (pointFacingY === "top"){
if (b1.y > b2.y) {
b2.y -= overlapY;
b2.stop();
}
else {
b1.y -= overlapY;
b1.stop();
}
}
else{
if (b1.y < b2.y) {
b2.y += overlapY;
b2.stop();
}
else {
b1.y += overlapY;
b1.stop();
}
}
}
}
I have a trail on a map that the user can "follow" by mousing over a graph (time and speed). If the user zooms in a lot, part of the trail may not be visible. When the user wants to see the part of the trail that is not showing I use the panTo method...
The panTo method of leaflet is currently also centering. I don't want to center, I want the map to move just enough to show a point. (The problem with panTo is it causes excessive map scrolling and a harsh user experience.)
I have tried changing the bounds, but that has an (unwanted) side affect of sometimes zooming out.
Any way I can do a "minimal" panTo?
This is a (working but unpolished) solution; map is our own map wrapper utility class, lmap is a leaflet map object in typescript, and toxy() is a method to convert lat/longs to x/y values.
if (!this.lmap.getBounds().contains(latlng)) {
let target = this.map.toxy(latlng);
let nw = this.map.toxy(this.lmap.getBounds().getNorthWest());
let se = this.map.toxy(this.lmap.getBounds().getSouthEast());
let x = 0, y = 0;
let margin = 75;
if (target.y < nw.y)
y = (-1 * (nw.y - target.y)) - margin;
else if (target.y > se.y)
y = (target.y - se.y) + margin;
if (target.x < nw.x)
x = (-1 * (nw.x - target.x)) - margin;
else if (target.x > se.x)
x = (target.x - se.x) + margin;
this.lmap.panBy(new L.Point(x, y));
}
First, fetch the bounds of the map (measured in pixels from the CRS origin) with map.getPixelBounds(). Then, use map.project(latlng, map.getZoom()) to get the coordinates (in pixels from the CRS origin) of the point you're interested.
If you're confused about this "pixels from the CRS origin" thing, read the "Pixel Origin" section at http://leafletjs.com/examples/extending/extending-2-layers.html .
Once you have these pixel coordinates, it should be a simple matter of checking whether the point is inside the viewport, and if not, how far away on each direction it is.
http://jsfiddle.net/jcollin6/b131tobj/2/
Should give you what you want
function clamp(n,lower,upper) {
return Math.max(lower, Math.min(upper, n));
}
//Shamelessly stolen from https://gist.github.com/dwtkns/d5b9b60285b8b0067c53
function getNearestPointInPerimeter(l,t,w,h,x,y) {
var r = l+w,
b = t+h;
var x = clamp(x,l,r),
y = clamp(y,t,b);
var dl = Math.abs(x-l),
dr = Math.abs(x-r),
dt = Math.abs(y-t),
db = Math.abs(y-b);
var m = Math.min(dl,dr,dt,db);
return (m===dt) ? {x: x, y: t} :
(m===db) ? {x: x, y: b} :
(m===dl) ? {x: l, y: y} : {x: r, y: y};
}
L.ExtendedMap = L.Map.extend({
panInside: function (latlng, pad, options) {
var padding = pad ? pad : 0;
var pixelBounds = this.getPixelBounds();
var center = this.getCenter();
var pixelCenter = this.project(center);
var pixelPoint = this.project(latlng);
var sw = pixelBounds.getBottomLeft();
var ne = pixelBounds.getTopRight();
var topLeftPoint = L.point(sw.x + padding, ne.y + padding);
var bottomRightPoint = L.point( ne.x - padding, sw.y - padding);
var paddedBounds = L.bounds(topLeftPoint, bottomRightPoint);
if (!paddedBounds.contains(pixelPoint)) {
this._enforcingBounds = true;
var nearestPoint = getNearestPointInPerimeter(
sw.x + padding,
ne.y + padding,
ne.x - sw.x - padding * 2,
sw.y - ne.y - padding * 2,
pixelPoint.x,
pixelPoint.y
);
var nearestPixelPoint = L.point(nearestPoint.x,nearestPoint.y)
var diffPixelPoint = nearestPixelPoint.subtract(pixelPoint);
var newPixelCenter = pixelCenter.subtract(diffPixelPoint);
var newCenter = this.unproject(newPixelCenter);
if (!center.equals(newCenter)) {
this.panTo(newCenter, options);
}
this._enforcingBounds = false;
}
return this;
}
});
Use this way
map.panTo([lat, lng]);
map.setZoom(Zoom);
I am using the latest version of Core Plot within a Swift program. The graph plots without a problem and shows the following
If you look at the plot the lower portion of the gradient fill for the plot does not match the xAxis which is the behaviour that I expect. instead it starts part way up the yAxis.
I have not experienced this behaviour before and am not sure where to hunt for the bug in my code. I haven't been able to find a relevant entry elsewhere within stackOverflow or elsewhere on the net for that matter.
I have tested the plotspace variables and they are on the money, min/max for the Y axis is as expected and the X/Y coords through the delegate functions shown below [number() and numberOfRecords()] are as expected. I am not sure what within core plot will be driving this behaviour.
If anyone is able to suggest other areas to examine for the error that would be greatly appreciated.
Rather than post all of the code initially, if someone can suggest an area to explore I will add that element of the code in to the question to help with the diagnosis. I have initially only included the code for the section of the app that defines the scatter line plot [configureLineGraph()] below the delegate functions below.
// Delegate functions for Core Plot
func numberOfRecords(for plot: CPTPlot) -> UInt {
numberOfRecordsToBeDisplayedOnGraph = UInt(arrayOfCommanderProgress.count)
return numberOfRecordsToBeDisplayedOnGraph // this needs to represent the number of records to be displayed on the graph
}
func number(for plot: CPTPlot, field fieldEnum: UInt, record idx: UInt) -> Any? {
let index = Int(numberOfRecordsToBeDisplayedOnGraph - idx) - 1
switch CPTScatterPlotField(rawValue: Int(fieldEnum))! {
case .X:
let xCoord = (arrayOfCommanderProgress[index].timeStamp.convertDateStringToDateObject()).timeIntervalSinceReferenceDate
return xCoord
case .Y:
var yCoord = Double()
if fieldEnum == 1 {
switch graphToDisplay {
case graphCombat:
yCoord = (Double(self.arrayOfCommanderProgress[index].combat)) + (100.0 * (Double(arrayOfRankProgress[index].combat)))
case graphTrade:
yCoord = (Double(self.arrayOfCommanderProgress[index].trade)) + (100.0 * (Double(arrayOfRankProgress[index].trade)))
case graphExploration:
yCoord = (Double(self.arrayOfCommanderProgress[index].explore)) + (100.0 * (Double(arrayOfRankProgress[index].explore)))
case graphFederation:
yCoord = (Double(self.arrayOfCommanderProgress[index].federation)) + (100.0 * (Double(arrayOfRankProgress[index].federation)))
case graphEmpire:
yCoord = (Double(self.arrayOfCommanderProgress[index].empire)) + (100.0 * (Double(arrayOfRankProgress[index].empire)))
case graphCQC:
yCoord = (Double(self.arrayOfCommanderProgress[index].cQC)) + (100.0 * (Double(arrayOfRankProgress[index].cQC)))
default:
break
}
}
return yCoord
}
}
// Funtions to set up the graphView and plots
func configureLineGraph() {
// Plotline variables for scatterplot
let plotLineTotal = CPTScatterPlot()
let areaGradient:CPTGradient = CPTGradient(beginning: CPTColor.clear(), ending: CPTColor(componentRed: 150/255.0, green: 180/255.0, blue: 200/255.0, alpha: 0.9))
areaGradient.angle = 90.0
let areaGradientFill:CPTFill = CPTFill(gradient: areaGradient)
// Set up plot lines for each type of total to be graphed
plotLineTotal.areaFill = areaGradientFill
// set up plotline characteristics
let plotLineType = CPTMutableLineStyle()
plotLineType.lineColor = CPTColor.darkGray()
// establish a reference to the graph custom view
guard let graph = graphView.hostedGraph else { return }
// Add additional plotlines to the plots Array if needed
let plots = [plotLineTotal]
for plot in plots {
plot.dataSource = self
plot.delegate = self
plot.areaBaseValue = CPTDecimalFromInteger(0) as NSNumber
plot.dataLineStyle = plotLineType
graph.add(plot, to: graph.defaultPlotSpace)
}
graph.reloadData()
}
I am building an app using Swift and SpriteKit. The app loads an SKSpriteNode that is exactly the size of the screen and places it to exactly cover the screen. However, the user can move and zoom this SKSpriteNode as much as they like. I care about the absolute position of the SKSpriteNode because if the node is moved too much I like to bring it back to center, or "snap" it to the edges of the screen. (The SKSpriteNode will eventually hold a map).
When the node has a scale of exactly 1.0, the position values work as I expect them to. However, when I zoom the node (using a pinch gesture) and the scale grows above 1.0, the position values do not make sense. Here are some images to illustrate what I mean.
Here, you can see that the un-zoomed node has the exact same dimensions and position as the scene it is placed in. X and Y are both 0 for each the Map as well as the scene:
However, as you can see in this next image, when I zoom the Map to be a larger SKSpriteNode, and move it so that the lower left corner of the Map is exactly placed in the lower left corner of the screen, the X and Y values of the zoomed node are off. I would expect the X to be 0 and the Y to be 0 but they are not.
What is happening here, and how do I calculate the absolute position of a zoomed node? And when I say "absolute" position, perhaps what I mean is the position relative to the scene?? Anyway, your thoughts are appreciated.
I figured this out, although it wasn't easy. It turned out to be a case of "AnchorPoint Dorkitis". The primary symptom of this condition is writing code based on a false assumption, and in my case, the assumption that did me in was this:
The (X,Y) point of my node was the lower left corner of the node. (false)
I knew all along that I set my AnchorPoints (of both the scene as well as the SKSpriteNode) to (x: 0.5, y: 0.5) but it had not sunk in for me yet that this also was altering where on the screen the (X,Y) coordinate actually lived (hint: the center).
For those who are interested, I worked out a nice set of computed properties to return the X, Y, height, width, top, bottom, left, and right values for my scene and my SKSpriteNode. I share you them, here:
var map_x: Int {
get {
return Int(self.position.x)
}
}
var map_y: Int {
get {
return Int(self.position.y)
}
}
var map_h: Int {
get {
return Int(self.frame.height)
}
}
var map_w: Int {
get {
return Int(self.frame.width)
}
}
var map_t: Int {
get {
return Int(self.position.y + self.frame.height/2)
}
}
var map_b: Int {
get {
return Int(self.position.y - self.frame.height/2)
}
}
var map_l: Int {
get {
return Int(self.position.x - self.frame.width/2)
}
}
var map_r: Int {
get {
return Int(self.position.x + self.frame.width/2)
}
}
var scene_x: Int {
get {
return Int((scene?.position.x)!)
}
}
var scene_y: Int {
get {
return Int((scene?.position.y)!)
}
}
var scene_h: Int {
get {
return Int((scene?.frame.height)!)
}
}
var scene_w: Int {
get {
return Int((scene?.frame.width)!)
}
}
var scene_t: Int {
get {
return Int((scene?.position.y)! + (scene?.frame.height)!/2)
}
}
var scene_b: Int {
get {
return Int((scene?.position.y)! - (scene?.frame.height)!/2)
}
}
var scene_l: Int {
get {
return Int((scene?.position.x)! - (scene?.frame.width)!/2)
}
}
var scene_r: Int {
get {
return Int((scene?.position.x)! + (scene?.frame.width)!/2)
}
}
I could have made them CGFloat values and not wrapped them in Int() but I needed ints elsewhere in the code so I figured may as well do that here. I think the next step for me is to figure out how to do this in fewer lines of code :)