is this a spot for functional lenses in javascript? - javascript

Playing around with point-free style javascript for fun.
Say I am coding the video game Diablo, and I am modeling enemies using complex nested types like this but deeper and more complicated:
{ name: "badguy1", stats: { health: 10: strength: 42 }, pos: {x: 100, y: 101 } }
So I have a list of all my enemies. I want to do damage to all the enemies within a certain radius
function isInRange(radius, point) { return point.x^2 + point.y^2 >= radius^2; }
function fireDamage(health) { return health - 10; }
var newEnemies = enemies.filter(isInRange).map(fireDamage);
this of course doesn't type check - my combinators take primitives, so i need to map and filter "down another level". I don't want to obscure the filter/map business logic pipeline. I know lenses can help me but lets say I am in a browser, as this is of course trivial with mutable structures. How do I do it?

Is your question is about how to use lenses in Javascript? If so, I may be able to help. Have you checked out the Ramda.js library? It's a terrific way to write functional JS. Let's start by looking at your enemy model:
/* -- data model -- */
let enemyModel = {
name: "badguy1",
stats: {
health: 10,
strength: 42
},
pos: {
x: 100,
y: 101
}
};
Lens: In order to construct a lens you need a getter method and a setter method for your specific object -- in your case the "enemy". Here's how you could construct those by hand.
Method 1: Create your own getters and setters
const getHealth = path(['stats', 'health']);
const setHealth = assocPath(['stats', 'health']);
const healthLens = lens(getHealth, setHealth);
Method 2: Ramda's expedient convenience-lens for Objects
const healthLens = lensPath(['stats', 'health']);
Once you've created the lens, it's time to use it. Ramda offers 3 functions for using lenses: view(..), set(..), and over(..).
view(healthLens)(enemyModel); // 10
set(healthLens, 15)(enemyModel); // changes health from 10 to 15
over(healthLens, fireDamage)(enemyModel); // reduces enemyModel's health property by 10
Since you're applying the fireDamage(..) function to an enemy's health, you'll want to use over(..). Also, since your position coordinates are nested within the enemyModel, you're going to want to use a lens to access those as well. Let's create one and refactor isInRange(..) while we're at it.
As a reference, here's the origin fn:
// NOTE: not sure if this works as you intended it to...
function isInRange(radius, point) {
return point.x^2 + point.y^2 >= radius^2; // maybe try Math.pow(..)
}
Here's a functional approach:
/* -- helper functions -- */
const square = x => x * x;
const gteRadSquared = radius => flip(gte)(square(radius));
let sumPointSquared = point => converge(
add,
[compose(square, prop('x')),
compose(square, prop('y'))]
)(point);
sumPointSquared = curry(sumPointSquared); // allows for "partial application" of fn arguments
/* -- refactored fn -- */
let isInRange = (radius, point) => compose(
gteRadSquared(radius),
sumPointSquared
)(point);
isInRange = curry(isInRange);
Here's what that would look like when dealing with a collection of enemyModels:
/* -- lenses -- */
const xLens = lensPath(['pos', 'x']);
const yLens = lensPath(['pos', 'y']);
const ptLens = lens(prop('pos'), assoc('pos'));
// since idk where 'radius' is coming from I'll hard-code it
let radius = 12;
const filterInRange = rad => filter(
over(ptLens, isInRange(rad)) // using 'ptLens' bc isInRange(..) takes 'radius' and a 'point'
);
const mapFireDamage = map(
over(healthLens, fireDamage) // using 'healthLens' bc fireDamage(..) takes 'health'
);
let newEnemies = compose(
mapFireDamage,
filterInRange(radius)
)(enemies);
I hope this helps illustrate how useful lenses can be. While there are many helper functions, I think the end piece of code is super semantic!
Lastly, I'm just flooding my scope with these functions from Ramda to make this example more readable. I'm using ES6 deconstruction to accomplish this. Here's how:
const {
add,
assocPath,
compose,
converge,
curry,
filter,
flip,
gte,
lens,
lensPath,
map,
over,
set,
path,
prop,
view
} = R;
// code goes below...
Try it out in jsBin! They offer Ramda support.

Read my article on lenses. It answers your question exactly the way you worded it. Seriously, I'm not even joking. Here's a code snippet from my post:
fireBreath :: Point -> StateT Game IO ()
fireBreath target = do
lift $ putStrLn "*rawr*"
units.traversed.(around target 1.0).health -= 3

Related

Detect if Matter.js Body is Circle

I want to test if a particular Matter body is a circle or not, as in:
const compounds = Matter.Composite.allBodies(engine.world)
compounds.forEach(compound => compound.parts.forEach(part => {
const isCircle = ???
if (isCircle) console.log(part.id, 'is a circle')
else console.log(part.id, 'is not a circle')
})
I can't find an official way to test if a Matter body was created as a circle. How can I test if a body was created with new Matter.Body.Circle versus another Body constructor?
You can console.log(a_circle) and check for something to identify a circle by.
I think you can check for a_circle.circleRadius or a_circle.label=='Circle Body'
EDIT: I have looked at the source code before posting this. It's a safe bet (for now as there is no documentation) because you can see that otherwise is just a polygon.
Matter.Bodies.circle = function(x, y, radius, options, maxSides) {
options = options || {};
var circle = {
label: 'Circle Body',
circleRadius: radius
};
// approximate circles with polygons until true circles implemented in SAT
maxSides = maxSides || 25;
var sides = Math.ceil(Math.max(10, Math.min(maxSides, radius)));
// optimisation: always use even number of sides (half the number of unique axes)
if (sides % 2 === 1)
sides += 1;
return Bodies.polygon(x, y, sides, radius, Common.extend({}, circle, options));
}
The other answer suggests looking for circle-specific properties. One problem is, these can change in future Matter.js releases. Also, it doesn't make for readable, intuitive code, and can lead to surprising bugs when additional body types wind up containing a property unexpectedly.
Better is to use the internal label property (also suggested in that answer), which should be stable and defaults to the seemingly-useful "Rectangle Body" and "Circle Body". For simple use cases, this works. Since it's possible to set the label to an object to store arbitrary custom data on the body, it's tempting to go further and use labels for just about everything.
However, I generally ignore labels. The reason is that it pushes too much of the client logic into a physics library that's not really designed for entity management. Furthermore, either of these approaches (labels or body-specific properties) involves iterating all of the bodies to filter out the type you're interested in.
Although no context was provided about the app, having to call allBodies often seems like a potential antipattern. It might be time to consider a redesign so you don't have to. What if you have 5 circles and 500 other bodies? Recursively iterating all 500 on every frame just to find the 5 is a huge waste of resources to achieve something that should be easy and efficient.
My preferred solution for entity management is to simply keep track of each type upon creation, putting them into data structures that are tuned to application-specific needs.
For example, the following script shows a method of efficiently determining body type by presenting the body as a key to a pre-built types map.
const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const map = {width: 300, height: 300};
const render = Matter.Render.create({
element: document.querySelector("#container"),
engine,
options: {...map, wireframes: false},
});
const rnd = n => ~~(Math.random() * n);
const rects = [...Array(20)].map(() => Matter.Bodies.rectangle(
rnd(map.width), // x
rnd(map.height), // y
rnd(10) + 15, // w
rnd(10) + 15, // h
{
angle: rnd(Math.PI * 2),
render: {fillStyle: "pink"}
}
));
const circles = [...Array(20)].map(() => Matter.Bodies.circle(
rnd(map.width), // x
rnd(map.height), // y
rnd(5) + 10, // r
{render: {fillStyle: "red"}}
));
const walls = [
Matter.Bodies.rectangle(
0, map.height / 2, 20, map.height, {
isStatic: true, render: {fillStyle: "yellow"}
}
),
Matter.Bodies.rectangle(
map.width / 2, 0, map.width, 20, {
isStatic: true, render: {fillStyle: "yellow"}
}
),
Matter.Bodies.rectangle(
map.width, map.height / 2, 20, map.height, {
isStatic: true, render: {fillStyle: "yellow"}
}
),
Matter.Bodies.rectangle(
map.width / 2, map.height, map.width, 20, {
isStatic: true, render: {fillStyle: "yellow"}
}
),
];
const rectangle = Symbol("rectangle");
const circle = Symbol("circle");
const wall = Symbol("wall");
const types = new Map([
...rects.map(e => [e, rectangle]),
...circles.map(e => [e, circle]),
...walls.map(e => [e, wall]),
]);
const bodies = [...types.keys()];
const mouseConstraint = Matter.MouseConstraint.create(
engine, {element: document.querySelector("#container")}
);
Matter.Composite.add(engine.world, [
...bodies, mouseConstraint
]);
const runner = Matter.Runner.create();
Matter.Events.on(runner, "tick", event => {
const underMouse = Matter.Query.point(
bodies,
mouseConstraint.mouse.position
);
if (underMouse.length) {
const descriptions = underMouse.map(e =>
types.get(e).description
);
document.querySelector("#type-hover").textContent = `
${descriptions.join(", ")} hovered
`;
}
else {
document.querySelector("#type-hover").textContent = `
[hover a body]
`;
}
if (mouseConstraint.body) {
document.querySelector("#type-click").textContent = `
${types.get(mouseConstraint.body).description} selected
`;
}
else {
document.querySelector("#type-click").textContent = `
[click and drag a body]
`;
}
});
Matter.Render.run(render);
Matter.Runner.run(runner, engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<h3 id="type-click">[click and drag a body]</h3>
<h3 id="type-hover">[hover a body]</h3>
<div id="container"></div>
If creating and destroying bodies can happen dynamically, a function would need to be written to handle data structure bookkeeping.
Another approach that might work well for some apps is to have a few type-specific sets or maps. This allows you to quickly access all entities of a particular type. This becomes particularly useful once you begin composing bodies as properties of custom classes.
There's no reason you can't have both structures--a reverse lookup that gives the type or custom class given a MJS body, and a structure that contains references to all entities or MJS bodies of a particlar type.
The reverse lookup could enable logic like "on click, take an action on a specific MJS body depending on its type" or "on click, locate my custom class/model associated with this MJS body", while collections support logic like "destroy all enemies".
Ideally, code shouldn't be doing much type-checking. With proper OOP design, you can implement classes that respond correctly to methods regardless of their type. For example, if you have Enemy and Ally classes that each respond to a click, you might create a method called handleClick in each class. This allows you to use code like clickedEntity.handleClick(); without having to know whether clickedEntity is an Enemy or Ally, thereby avoiding the need for a "get type" operation entirely.
For more design suggestions for Matter.js projects, see:
How do you access a body by its label in MatterJS?
My own model in matter.js

Is writing a JavaScript class wherein a single method can both get and set a property bad practice?

I'm writing some classes for a game and I'm not sure if writing a JavaScript class wherein it's methods can be used to set or retrieve one of it's classes members is good practice. I'm trying to understand why or not not.
I've implemented this on jsfiddle already, also posted at:
https://jsfiddle.net/97g16hq3/7/
Specifically, the methods x(x = null) and y(y = null) are the ones I'm referring to.
class Vector {
constructor( x, y ) {
this._x = x
this._y = y
}
x(x = null) {
if (x) {
this._x = x
} else {
return this._x
}
}
y(y = null) {
if (y) {
this._y = y
} else {
return this._y
}
}
}
const myVec = new Vector( 1, 2 )
console.log(myVec)
// Get the vector
console.log('x: ', myVec.x())
console.log('y: ', myVec.y())
// Set the vector
myVec.x(3)
myVec.y(4)
// Get the vector
console.log('new vector')
console.log('x: ', myVec.x())
console.log('y: ', myVec.y())
Better go standard and to use get and set.
Among the other reasons, IDEs are based on standards for many of their helpers (code completion, hints...), so it is better to take advantage of that.
Technically speaking, nothing is wrong with your approach. jQuery uses it extensively: .val(), .text(), .height() are just an example.
The library has been there for ages and as far as I know nobody ever spotted any downside about it.
If you're asking specifically about getting/setting, then I'd rather use getter/setter properties instead of standard methods overloaded with alternate behaior. That way the syntax at the "call" site makes clear what is going on.
class Vector {
constructor( x, y ) {
this._x = x
this._y = y
}
get x() {
return this._x
}
set x(x) {
this._x = x
}
get y() {
return this._y
}
set y(y) {
this._y = y
}
}
const myVec = new Vector( 1, 2 )
console.log(myVec)
// Get the vector
console.log('x: ', myVec.x)
console.log('y: ', myVec.y)
// Set the vector
myVec.x = 3
myVec.y = 4
// Get the vector
console.log('new vector')
console.log('x: ', myVec.x)
console.log('y: ', myVec.y)
In general, I'd avoid that kind of overloaded behavior, especially if the behavior differs wildly. It adds confusion to an API. IMO, the method name should describe what's happening.
It doesn't mean that you're assigning the value:
x(x = null) { // it's default value for x is null
So, this sets the argument x value to null if you pass no value or undefined.
Check default parameters for reference.
Edit:
It matters on your requirement. The following sets the new value if it has value:
if (x) {
this._x = x
} else {
return this._x
}
But this will set the value and doesn't care if it is null, undefined or anything else:
this._x = x // may result in `null`, `undefined`, ...
But it's really unnecessary to set the default value for x: x=null. Because, you're just checking it with if(x){.
In some case, you may particularly check for null and in that case setting the default value of x would be a way to go with:
if(x===null){
I see three problems with this class.
Imo. the biggest problem with that code is not this part x(x = null) {...} but this one
if (x) {
this._x = x
} else {
return this._x
}
Let's start with the most obvious: Is vector.x(0) setting the x-value to 0? What about vector.x("0")?
Then, when you add logic to your function to always cast the argument to a number, what about NaN, "", [], +[], {}, null and undefined? And believe me, several of these values will end up there.
The problem I see here is that your function starts getting a hard time to determine wehther it should set or get the value.
Overloading a function means additional logic that has to run, every time the function is called. Generally speaking this is no problem, but here we're talking about the getter and setter for the properties of a Vector in a game engine. This functions will be called thousands of times every frame. This is a considerable performance impact.
And this is related to #2. That you use a class Vector at all for this. I'd make this as lightweight as possible and simply use literals for this:
const myVec = { x: 1, y: 2 };
And one tip on the road, your code doesn't contain many ; you should start using them. Even if the engine inserts them mostly correct, eventually there will be situations where your code doesn't mean what you meant without them.

Javascript : optimize data structure for chess game

Trying to figure out how to store some useful data for chess games programming.
I decided to store rays emitted by on-board chessmen in a Raycaster; This question is about the implementation of this structure.
TL;DR (for chess gamers only...)
first of all, I have identified three kinds of rays :
Ray.NORMAL or Ray.FULL: they are emitted by all chessmen but not pawns, in an iterative way (rook, bishop, queen) or not (knight and king)
Ray.CAPTURE: emitted only by pawns, ahead left and /or ahead right captures
Ray.OFFSET: emitted by pawns when moving forward, and kings/rooks for castling
Thus a ray is defined like this :
class Ray {
constructor (owner, kind) {
this.owner = owner // chessman which emits ray
this.kind = kind
this.ref = null
this.sequence = []
}
// is computed afetr construction
expand (ref, sequence) {
this.ref = ref // starting ref (origin of the ray)
this.sequence = sequence // array of refs
}
// is called when ray is inserted into raycaster
interact (otherRay) {
// to be implemented
}
}
Rays also have two special compound properties :
shadowing { ray: null, idx: -1 }
crossing { ray: null, idx: -1 }
which denote where this ray instance may be shadowed by another chessman, and where another ray is crossing it, to detect passability and interference (for castling)
THE PROBLEM:
How to store efficiently rays in the RayCaster?
In a way that optimizes operations such as:
adding a newly computed ray, computing interactions with previously stored ones, at a mo,opmal cost?
determining from a given starting ref, all targeted tiles/ref?
determining easily which rays target a given ref, to compute pression balance on this tile?
PROPOSED SOLUTIONS / ALTERNATIVES
single array of rays : worst case 64 * 63 elements, costful for seeking a ray and compute interactions
Map of arrays : Map.set(startingRef, [list_of_emtted_rays_from_startingRef])
Map of arrays : Map.set(endingRef, [list_of_targetinhg_rays_to_endingRef])
and maybe a good candidate :
maintain 2 maps of arrays, one for emitted, and one for targeting
class RayCaster {
constructor() {
this.startings = new Map()
this.endings = new Map()
}
cast(board) { ...iterate through board and casts individual rays }
add(ray) { ... }
getRefsAttackedBy(ref) { ... }
getRefsAttacking(ref) { ... }
}
So what are your feelings about this data structure (the RayCaster)?
finally since Maps are time constant access, I have considered a double-maps implementation :
constructor() {
this.startings = new Map()
this.endings = new Map()
this.counters = { rays: 0, interactions: 0 }
}
Each map is keyed by board refs, from "a1" to "h8"
casts(board) {
this.counters = {
rays: this.raysFrom(board),
interactions: this.computeInteractions()
}
}
Adding rays is straighforward :
raysFrom(board) {
let counter = 0;
board.traverse((ref, chessman) => {
let rays = chessman.cast(ref)
for(let ray of rays) {
this.add(ray)
}
counter += rays.length
})
return counter
}
And a a simple ray :
add (ray) {
let skey = ray.ref
let sRays = this.startings.get(sKey)
if(sRays.indexOf(ray) === -1) {
sRays.push(ray)
}
ray.traverse((seqIdx, seqRef) => {
let seqKey = seqRef.key
let eRays = this.endings.get(seqKey)
if (eRays.indexOf(ray) === -1) {
eRays.push(ray)
}
})
}
Computing ray interactions (crossing and shading) is more complicated :
computeInteractions() {
let counter = 0
// consider all starting rays
for (let {sRef, sRays} of this.startings) {
for (let sRay of sRays) {
sRay.traverse((seqIdx, seqRef) => {
// consider all possible intersections
// into the endings map
let eRays = this.endings.get(seqRef.ref)
for(let eRay of eRays) {
// ensure that rays are different
if (sRay !== eRay) {
sRay.interact(eRay)
eRay.interact(sRay)
}
}
})
}
}
return counter
}
The rest of the work is just determining in the ray class how two rays could interact (crossing or shading)
Thanks for advice, best regards !

How can d3.transform be used in d3 v4?

In d3.js v4 the d3.transform method has been removed, without any hint about how to replace it.
Does anyone know how to replace the following d3.js v3 code?
d3.transform(String).translate;
Edit 2016-10-07: For a more general approach see addendum below.
According to the changelog it is gone. There is a function in transform/decompose.js, though, which does the calculations for internal use. Sadly, it is not exposed for external use.
That said, this is easily done even without putting any D3 to use:
function getTranslation(transform) {
// Create a dummy g for calculation purposes only. This will never
// be appended to the DOM and will be discarded once this function
// returns.
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
// Set the transform attribute to the provided string value.
g.setAttributeNS(null, "transform", transform);
// consolidate the SVGTransformList containing all transformations
// to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
// its SVGMatrix.
var matrix = g.transform.baseVal.consolidate().matrix;
// As per definition values e and f are the ones for the translation.
return [matrix.e, matrix.f];
}
console.log(getTranslation("translate(20,30)")) // simple case: should return [20,30]
console.log(getTranslation("rotate(45) skewX(20) translate(20,30) translate(-5,40)"))
This creates a dummy g element for calculation purposes using standard DOM methods and sets its transform attribute to the string containing your transformations. It then calls .consolidate() of the SVGTransformList interface to consolidate the possibly long list of transformation to a single SVGTransform of type SVG_TRANSFORM_MATRIX which contains the boiled down version of all transformations in its matrix property. This SVGMatrix per definition holds the values for the translation in its properties e and f.
Using this function getTranslation() you could rewrite your D3 v3 statement
d3.transform(transformString).translate;
as
getTranslation(transformString);
Addendum
Because this answer has gained some interest over time, I decided to put together a more general method capable of returning not only the translation but the values of all transformation definitions of a transform string. The basic approach is the same as laid out in my original post above plus the calculations taken from transform/decompose.js. This function will return an object having properties for all transformation definitions much like the former d3.transform() did.
function getTransformation(transform) {
// Create a dummy g for calculation purposes only. This will never
// be appended to the DOM and will be discarded once this function
// returns.
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
// Set the transform attribute to the provided string value.
g.setAttributeNS(null, "transform", transform);
// consolidate the SVGTransformList containing all transformations
// to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get
// its SVGMatrix.
var matrix = g.transform.baseVal.consolidate().matrix;
// Below calculations are taken and adapted from the private function
// transform/decompose.js of D3's module d3-interpolate.
var {a, b, c, d, e, f} = matrix; // ES6, if this doesn't work, use below assignment
// var a=matrix.a, b=matrix.b, c=matrix.c, d=matrix.d, e=matrix.e, f=matrix.f; // ES5
var scaleX, scaleY, skewX;
if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
return {
translateX: e,
translateY: f,
rotate: Math.atan2(b, a) * 180 / Math.PI,
skewX: Math.atan(skewX) * 180 / Math.PI,
scaleX: scaleX,
scaleY: scaleY
};
}
console.log(getTransformation("translate(20,30)"));
console.log(getTransformation("rotate(45) skewX(20) translate(20,30) translate(-5,40)"));
If you pull in d3 v4 through npm, you can import the src/transform/parse file directly and call parseSvg:
// using es2015 modules syntax
import { parseSvg } from "d3-interpolate/src/transform/parse";
parseSvg("translate(20, 20)");
On elements which have the d3.js zoom listener on them -- usually the <g> element appended to the svg element -- you can use this call to get the transformation attributes outside of the zoom function:
var self = this;
var t = d3.zoomTransform(self.svg.node());
// t = {k: 1, x: 0, y: 0} or your current transformation values
This returns the same values as when calling d3.event.transform within the zoom event function itself.
Calling d3.event.transform outside the zoom event function will error:
Uncaught TypeError: Cannot read property 'transform' of null
I have to use d3.zoomTransform to allow panning and zooming from buttons outside the graph.
I found a little bit simpler solution than that.
selection.node().transform.baseVal[0].matrix
In this matrix you have cordinates e and f witch are equivalent to x, y. (e === x, f === y). No need to implement your very own funtion for that.
baseVal is a list of transformations of the element. You can't use that for the object without previus transformation! (the list will be empty) Or if you done many tranformation to the object the last position will be under the last element of baseVal list.
I am a little late to the party, but I had some code that was beneficial to me, I hope it helps you out too.
The code above by #altocumulus is quite thorough and works like a charm. However it didn't quite meet my needs since I was doing the calculations by hand and needed to alter some transform properties as painlessly as possible.
This might not be the solution for everyone, but it was perfect for me.
function _getTokenizedTransformAttributeValue(transformStr) {
var cleanedUpTransformAttrArr = transformStr.split(')', ).slice(0,-1);
return cleanedUpTransformAttrArr.reduce(function(retObj, item) {
var transformPair = item.split('(');
retObj[transformPair[0]] = transformPair[1].split(',');
return retObj;
}, {});
}
function _getStringFromTokenizedTransformAttributeObj(transformAttributeObj) {
return Object.keys(transformAttributeObj).reduce(function(finalStr, key) {
// wrap the transformAttributeObj[key] in array brackets to ensure we have an array
// join will flatten the array first and then do the join so [[x,y]].join(',') -> "x,y"
return finalStr += key + "(" + [transformAttributeObj[key]].join(',') + ")";
}, '');
}
The really great thing with the first function is that I can manually alter a specific property (e.g. rotation), and not have to worry about how it affects translate or anything else (when rotating around a point), whereas when I rely on the built-in or even d3.transform methods they consolidate all the properties into one value.
Why is this cool?
Imagine a some HTML
<g class="tick-label tick-label--is-rotated" transform="translate(542.8228777985075,0) rotate(60, 50.324859619140625, 011.402383210764288)" style="visibility: inherit;"></g>
Using d3.transfrom I get:
In object form
jr {rotate: 59.99999999999999, translate: [577.8600589984691, -37.88141544673796], scale: [1, 1], skew: 0, toString: function}
In string form
"translate(577.8600589984691,-37.88141544673796)rotate(59.99999999999999)skewX(0)scale(1,1)"
Which is correct mathematically, but makes it hard for me to simply remove the angle of rotation and the translation that had to be introduced to rotate this element around a given point.
Using my _getTokenizedTransformAttributeValue function
In object form
{translate: ["542.8228777985075", "0"], rotate: ["60", " 50.324859619140625", " 011.402383210764288"]}
In string form using the function _getStringFromTokenizedTransformAttributeObj
"translate(542.8228777985075,0)rotate(60, 50.324859619140625, 011.402383210764288)"
Which is perfect because now when you remove the rotation, your element can go back to where it was
Granted, the code could be cleaner and the function names more concise, but I really wanted to get this out there so others could benefit from it.
I found a way do achieve something similar by using this:
d3.select(this).node().getBBox();
this will give you access to the x/y position and width/height
You can see an example here: https://bl.ocks.org/mbostock/1160929

How do I JSON encode only part of a Javascript object?

I'm writing a 2D gravity simulation game and I'm trying to add save/load functionality. In the game I store all of the current planets in an array. Each planet is represented by a Body object which contains the coordinates, mass, and motion vector of the planet. It also stores an array of the last 100 coordinates of the planet in order to draw the planet's trail on the screen.
I want to use JSON.stringify() to serialize the planets array. I'd like to save the first attributes of each planet (mass, location, motion) but I don't need to save the last 100 coordinates (the trail array). I don't want to completely delete the coordinates otherwise the trails will disappear from the screen. Can I stringify only a portion of each object? If not, can I remove that portion of the JSON string after it's been encoded? Or should I move the coordinates elsewhere during the save process then copy them back into each planet once it's been saved?
In modern web browsers you can use Array#map.
var serialized = JSON.stringify(planets.map(function(planet){
return {
mass: planet.mass,
location: planet.location,
motion: planet.motion
};
}));
Or, the equivalent using a for loop.
try it this way
var saved = JSON.stringify( {mass:body.mass,location:body.location,motion:body.motion} );
it shall give you just the three parts as a json string.
A bit more extended you could provide your body class such an export function.
For example:
Bodyclass.export = function( toexport ) {
if ( undefined === toexport || toexport.constructor != Array ) {
var toexport = [ 'mass', 'location', 'motion' ];
}
var export = {};
for ( var i = 0; i < toexport; i++) {
export[ toexport[ i ] ] = this[ toexport[ i ] ];
]
}
var saved = JSON.stringify( body.export() );
The best would be to create both a serialization and deserialization method. This will allow you to create the most efficient storage format while still allowing you to reconstruct as much of the objects as you deem necessary.
You can use export/import, save/restore, serialize/deserialize terminology, whichever you see fit.
Having methods like this will increase you maintainability in the long run as well.
You can use second parameter of JSON.stringify (replacer)
const planet = {
name: "Pluto",
lastCoords: [[0, 0], [1,1,]]
}
const json = JSON.stringify(planet, (key, value) => key === "lastCoords" ? undefined : value)
// json === {"name":"Pluto"}

Categories

Resources