How to shift object in Immutable Js Ordered map.? - reactjs

I am working with redux, configured state as Immutable Js Ordered Map.
Below is the state:
{
"layers": {
"items": {
"yrgroih9": {
"width": '300px',
"x": '700px',
"y": '700px'
},
"qhy0dukj": {
"width": '160px',
"x": '500px',
"y": '500px'
},
"7lw2nvma": { //Target object
"width": '250px',
"x": '300px',
"y": '300px'
},
"jdkhsd9d": {
"width": '280px',
"x": '100px',
"y": '100px'
}
}
}
}
I want to shift the key 7lw2nvma one step up, & below is i want:
{
"layers": {
"items": {
"yrgroih9": {
"width": '300px',
"x": '700px',
"y": '700px'
},
"7lw2nvma": { //Shifted object
"width": '250px',
"x": '300px',
"y": '300px'
},
"qhy0dukj": {
"width": '160px',
"x": '500px',
"y": '500px'
},
"jdkhsd9d": {
"width": '280px',
"x": '100px',
"y": '100px'
}
}
}
}
Reducer code:
import { OrderedMap, fromJS } from 'immutable';
export default function myApp(state = new OrderedMap, action) {
switch (action.type) {
case 'SHIFT_OBJECT':
var shiftObj = "7lw2nvma"; //Known value
//return state.getIn(["layers","items"]).shift(shiftObj); // Something like this to shift object.
}
}
Help me to get the desired output.
Note: If it is not possible in OrderedMap, then say with List.

I found answer based on sortBy function in Immutable Js OrderedMap.
Now i added index values in that object as shown below:
{
"layers": {
"items": {
"yrgroih9": {
"index":0,
"width": '300px',
"x": '700px',
"y": '700px'
},
"qhy0dukj": { //Another known value
"index":1,
"width": '160px',
"x": '500px',
"y": '500px'
},
"7lw2nvma": { //Target object (shift up)
"index":2,
"width": '250px',
"x": '300px',
"y": '300px'
},
"jdkhsd9d": {
"index":3,
"width": '280px',
"x": '100px',
"y": '100px'
}
}
}
}
I have two known values 7lw2nvma & qhy0dukj.
Using setIn changed 7lw2nvma index as 1 & qhy0dukj index as 2.
Using sortBy function i can sort object based on modified index values.
var getSorted = state.getIn(["layers","items"]).sortBy(function(value, key) {
return value.get("index");
});
And now the state items sorted successfully.

You are trying to modify directly your state. That is forbidden for an immutable.
You can use Object.assign to create a new one.
return Object.assign({}, state.getIn(["layers","items"]).shift(shiftObj))
EDIT: You can use an array of your keys, sort it as you like and construct a new object for your state.
Something like:
let tab = Object.keys(state.getIn(["layers","items"]))
tab.sort() // sort keys by name
const obj = tab.reduce((o, key) => Object.assign(o, {[key]: state.getIn(["layers","items"])[key]}), {}) //contruct a new object from your ordered array and current state, you can also create another const on state.getIn(["layers","items"]) to increase perfs
console.log(obj)
return obj

Related

Combine multiple x / y in SVG

Given this object, i wan't to merge all possible start and endpoints with a distance of x, thus creating three paths given below objects. The function only seems to see half of the connections.
The model below would output three elements in maker js using chaining, i wan't to recreate that.
How could i improve this function?
let mergedPaths = [
{
"id": 2,
"path": "M 19.8279000000 0.0000000000L -19.8300000000 0.0000000000",
"startPoint": {
"x": "19.8279000000",
"y": "0.0000000000",
},
"endPoint": {
"x": "-19.8300000000",
"y": "0.0000000000"
}
},
{
"id": 3,
"path": "M 24.8250000000 -5.1691000000L 24.8230000000 -5.1691000000",
"startPoint": {
"x": "24.8250000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
}
},
{
"id": 4,
"path": "M -17.9684000000 -65.0000000000L 17.9664000000 -65.0000000000",
"startPoint": {
"x": "-17.9684000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "17.9664000000",
"y": "-65.0000000000",
}
},
{
"id": 5,
"path": "M -24.8271000000 -5.1691000000L -22.9656000000 -60.1691000000",
"startPoint": {
"x": "-24.8271000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "-22.9656000000",
"y": "-60.1691000000",
}
},
{
"id": 6,
"path": "M 22.9615000000 -60.1691000000L 24.8230000000 -5.1691000000",
"startPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
}
},
{
"id": 7,
"path": "M 22.9615000000 -60.1691000000L 22.9635000000 -60.1691000000L 22.9635000000 -60.1691000000L 24.8250000000 -5.1691000000L 24.8250000000 -5.1691000000A 5.0000000000 5.0000000000 0 0 1 19.8279000000 0.0000000000",
"startPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "19.8279000000",
"y": "0.0000000000",
}
},
{
"id": 8,
"path": "M 24.8230000000 -5.1691000000A 5.0000000000 5.0000000000 0 0 1 19.8259000000 0.0000000000",
"startPoint": {
"x": "24.8230000000",
"y": "-5.1691000000",
},
"endPoint": {
"x": "19.8259000000",
"y": "0.0000000000",
}
},
{
"id": 9,
"path": "M 17.9643000000 -65.0000000000A 5.0000000000 5.0000000000 0 0 1 22.9615000000 -60.1691000000",
"startPoint": {
"x": "17.9643000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "22.9615000000",
"y": "-60.1691000000",
}
},
{
"id": 10,
"path": "M -22.9656000000 -60.1691000000A 5.0000000000 5.0000000000 0 0 1 -17.9684000000 -65.0000000000",
"startPoint": {
"x": "-22.9656000000",
"y": "-60.1691000000",
},
"endPoint": {
"x": "-17.9684000000",
"y": "-65.0000000000",
}
},
{
"id": 11,
"path": "M -19.8300000000 0.0000000000A 5.0000000000 5.0000000000 0 0 1 -24.8271000000 -5.1691000000",
"startPoint": {
"x": "-19.8300000000",
"y": "0.0000000000",
},
"endPoint": {
"x": "-24.8271000000",
"y": "-5.1691000000",
}
},
{
"id": 12,
"path": "M 17.9664000000 -65.0000000000A 5.0000000000 5.0000000000 0 0 1 22.9635000000 -60.1691000000",
"startPoint": {
"x": "17.9664000000",
"y": "-65.0000000000",
},
"endPoint": {
"x": "22.9635000000",
"y": "-60.1691000000",
}
},
{
"id": 13,
"path": "M 0.0000000000 -48.7500000000A 1.2500000000 1.2500000000 0 0 1 0.0000000000 -51.2500000000",
"startPoint": {
"x": "0.0000000000",
"y": "-48.7500000000",
},
"endPoint": {
"x": "0.0000000000",
"y": "-51.2500000000",
}
}
]
let pointMatchingDistance = 1;
getDistance = (x1: number, x2: number, y1: number, y2: number) => {
let y = x2 - x1;
let x = y2 - y1;
return Math.sqrt(x * x + y * y);
}
for (let mergedPath of mergedPaths) {
for (let mergedPathCheck of mergedPaths) {
if (!mergedPathCheck.mergedWith && !mergedPath.mergedWith) {
let distanceStartToEndCheck = this.getDistance(
mergedPathCheck.endPoint?.x,
mergedPath.startPoint?.x,
mergedPathCheck.endPoint?.y,
mergedPath.startPoint?.y,
);
if (distanceStartToEndCheck <= pointMatchingDistance) {
mergedPaths = mergedPaths.map((mp) => {
return mp.id == mergedPath.id ? {
...mp,
path: mergedPathCheck.path + mergedPath.path.replace("M", "L"),
startPoint: mergedPathCheck.startPoint,
mergedWith: mergedPathCheck.id
} : mp
}
)
}
}
}
}
I'll do something that is not very helpfull most of the time - show you a completely different code solving the same problem. But it's incidentally something that I have recently written, and that is relatively thoroughly tested by now.
It takes into account some special cases that might or might not be applicable to your data:
No prior knowledge about the order of the paths in the collection. A path to be "stitched" to another one may be earlier or later in the array.
No prior knowledge whether successfull stitching will involve matching path1.endPoint to path2.startPoint or path2.endPoint to path1.startPoint.
Stitching might even only be possible if you match path1.startPoint to path2.startPoint or path1.endPoint to path2.endPoint, which means you have to reverse the direction of one of the paths.
A path may close in on itself, when path1.endPoint and path1.startPoint are within matching distance (before or after stitching multiple paths together).
As you can see, there are a lot of cases how the paths might fit together, and probably your code missed some of them.
Another difference is that your code tests the condition
if (!mergedPathCheck.mergedWith && !mergedPath.mergedWith)
Since you are changing existing paths, this means that no more than two paths can ever be stitched together - a resulting path made up of three or more parts can never happen. My code works like this:
splice an original path off of the source array (called heap), and add it to a target array (chains) as a new, unconnected path
run through the heap searching for paths matching the last path in chains. If you find one, stitch it onto the path. and run 2. again.
If you find none, go back to 1., until heap is empty.
One method is missing: Chainer.prototype.reverse(path). My original code had the path data as collections of point objects, not as strings. Therefore I can't provide you with code for reversing a path string.
class Chainer {
chains = [];
pointMatchingDistance = 1;
constructor (heap) {
while (heap.length) {
const { id, path, startPoint, endPoint } = heap.shift();
// start a new ordered array of matching paths
const ordered = {
paths: [{d: path, startPoint, id}],
closed: this.matchPoints(first, startPoint, endPoint),
endPoint
};
// recursively add matching paths to the ordered array
this.findNext(ordered, heap, startPoint, endPoint);
// after no more matching paths are found,
// add the ordered array to chains
this.chains.push(ordered);
}
}
matchPoints(p1, p2) {
const y = p2.x - p1.x;
const x = p2.y - p1.y;
return Math.hypot(x, y) <= this.pointMatchingDistance;
}
reverse(path) {
// TBD
}
// splice a path off heap and reverse its direction if needed
getNext(heap, idx, reverse) {
const add = heap.splice(idx, 1)[0];
if (reverse) {
return {
...add,
path: this.reverse(add.path),
startPoint: add.endPoint,
endPoint: add.startPoint
};
} else {
return {...add};
}
}
// add the path at the end of ordered
addNext(ordered, nextWay) {
const { id, path, startPoint, endPoint } = nextWay;
ordered.paths.push({d: path, first, id});
ordered.endPoint = endPoint;
return endPoint;
}
// add the path at the start of ordered
addPrev(ordered, prevWay) {
const { id, path, startPoint, endPoint } = prevWay;
ordered.paths.unshift({d: path, startPoint, id});
return startPoint;
}
// search for a matching path in the heap and identify the direction
findNext(ordered, heap, startPoint, endPoint) {
let next, prev, reverseNext, reversePrev;
for (const [idx, way] of heap.entries()) {
if(this.matchPoints(way.startPoint, endPoint)) {
next = idx;
break;
}
if (this.matchPoints(way.endPoint, startPoint)) prev = idx;
if (this.matchPoints(way.endPoint, endPoint)) reverseNext = idx;
if (this.matchPoints(way.startPoint, startPoint)) reversePrev = idx;
}
if (next !== undefined) {
endPoint = this.addNext(ordered, this.getNext(heap, next, false));
} else if (prev !== undefined) {
startPoint = this.addPrev(ordered, this.getNext(heap, prev, false));
} else if (reverseNext !== undefined) {
endPoint = this.addNext(ordered, this.getNext(heap, reverseNext, true));
} else if (reversePrev !== undefined) {
startPoint = this.addPrev(ordered, this.getNext(heap,reversePrev, true));
// if none of the matching criteria has been met, return
} else return;
// paths closing in on themselves can have no more matching paths
if (this.matchPoints(startPoint, endPoint)) {
ordered.closed = true;
return;
}
// try to find more matching paths
this.findNext(ordered, heap, startPoint, endPoint);
}
// take the ordered arrays in chains
// and join them to one path data string each
writePaths() {
return this.chains.map(ordered => {
let mergedPath;
ordered.paths.forEach(({d, id}) => {
if (mergedPath) {
mergedPath.d += d.replace("M", "L");
} else {
mergedPath = { d, id };
}
});
if (ordered.closed) mergedPath += "Z";
return mergedPath;
});
}
}
const chainer = new Chainer(paths);
const mergedPaths = chainer.writePaths();

Expo BarcodeScanner via Camera returning wrong bounds

I have been attempting to utilize the barcode scanner via the Camera exported by expo-camera due to being able to freeze the scanner upon scanning a valid QRCode. The problem is, the bounds being returned for the scanned region are incorrect.
Here is my usage of the Camera component:
<Camera
flashMode={flashMode}
barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}
onBarCodeScanned={scannedRegion ? undefined : handleQRScanned}
style={{
width: "100%",
height: "100%",
}}
ref={(el) => setScanner(el)}
>
The handleQRScanned method is just logging the output.
And here is what the scanned object looks like:
{
"bounds": Object {
"origin": Object {
"x": 0.7085477709770203,
"y": 0.2856549024581909,
},
"size": Object {
"height": 0.41216737031936646,
"width": 0.23583674430847168,
},
},
"cornerPoints": Array [
Object {
"x": 0.7085477791037765,
"y": 0.6859185008442614,
},
Object {
"x": 0.9367770043059619,
"y": 0.69782228348775,
},
Object {
"x": 0.9443845063336068,
"y": 0.29520473921943935,
},
Object {
"x": 0.7161692883258375,
"y": 0.28565489300913066,
},
],
"data": "exp://192.168.1.4:19000",
"target": 5303,
"type": "org.iso.QRCode",
}
Also, when previously using the BarcodeScanner exported by expo-barcode-scanner directly, the bounds come out correct. Has anyone experienced this before?
I had the same issue, I solved it by multiplying bound values with screen height and width from Dimensions API. By this way, you can correctly localize barcodes in screen. Here is the code snippet. Height and width are inversely proportioned meaning that bounds.size.width actually corresponds to screen height and vice versa.
height: Number(bounds.size.width) * deviceHeight,
width: Number(bounds.size.height) * deviceWidth,
right: Number(bounds.origin.y) * deviceWidth,
top: Number(bounds.origin.x) * deviceHeight,

VictoryPie Custom Colors

I'm using VictoryPie to show my data, however, I want to be able to choose which color represents which section. Mainly because this is a pie chart of colors. I have seen a couple questions on StackOverflow asking something similar, but none of them fixed my problem.
My data object looks like:
[
{
"color": "#0000fe",
"x": "blue",
"y": 1
},
{
"color": "#fe00fe",
"x": "fuchsia",
"y": 2
},
{
"color": "#fefe00",
"x": "yellow",
"y": 8
},
{
"color": "#008000",
"x": "green",
"y": 20
}
]
And my code to render it is:
<VictoryPie
data={this.state.sets}
style={{
data: { fill: (d) => d.color }
}}
/>
My Pie currently looks entirely black, and I wanted to know if I was doing something wrong.
you're taking a color from the wrong place, try this:
<VictoryPie
data={this.state.sets}
style={{
data: { fill: (d) => d.datum.color }
}}
/>

Bar series not rendering correctly under label using the react-vis library

I'm running into a problem. The bar series is not coming right under it's label. I can't figure out what is going wrong.
Here is the code for it
const FlexibleXYPlot = makeWidthFlexible(XYPlot);
<FlexibleXYPlot height={graphContainer.height} margin={graphContainer.margin} xDistance={0} xType="ordinal">
<HorizontalGridLines />
<XAxis/>
<YAxis orientation="right" style={yAxisStyles} />
{ this.state.data.map((lineData, i) => (
<VerticalBarSeries
key={i}
data={lineData.timeline}
/>
)); }
</FlexibleXYPlot>
and the data is this
[
{
"timeline": [
{
"x": "dataA",
"y": 12.21
}
]
},
{
"timeline": [
{
"x": "dataB",
"y": 21.09
}
]
},
{
"timeline": [
{
"x": "dataC",
"y": 16.66
}
]
}
I figured it out.
For each bar I was creating a separate series. I just had to pass the whole data to a single series. Now if somebody wants each bar to have a different color in a single series they can pass the color with the data like given below
[
{
"x": "dataA",
"y": 12.21,
"color": <something>
},
{
"x": "dataB",
"y": 21.09,
"color": <something>
},
{
"x": "dataC",
"y": 16.66,
"color": <something>
}
]
and we need to pass colorType="literal" to the series itself.

mapbox-gl.js with PGRestAPI vector tile(pbf)

I have own vector tile from PGRestAPI, url like below
"http://192.168.1.4:3001/services/postgis/cleantech2/geom/vector-tiles/{z}/{x}/{y}.pbf"
and I try use mapbox-gl.js to render the map, but nothing display.
I am doing wrong? thx
var style = {
"version": 8,
"sources": {
"countries": {
"type": "vector",
"tiles": ["http://192.168.1.4:3001/services/postgis/cleantech2/geom/vector-tiles/{z}/{x}/{y}.pbf"],
"maxzoom": 6
}
},
"glyphs": location.origin+location.pathname+"font/{fontstack}/{range}.pbf",
"layers": [{
"id": "background",
"type": "background",
"paint": {
"background-color": "#ddeeff"
}
},{
"id": "country-glow-outer",
"type": "line",
"source": "countries",
"source-layer": "country",
"layout": {
"line-join":"round"
}
}]
};
var init_lat = 1.3552799//42.299228067198634;
var init_lng = 103.6945413;//-83.69717033229782;
mapboxgl.accessToken = 'mapbox-token';
var map = new mapboxgl.Map({
container: 'map',
style: style,
center: [init_lng,init_lat],
zoom: 15
});
edit 1:
after debug mapbox-gl-js code, now can see several circles. I modify the style, the source-layer name from pbf must be correct.
but not display all the points, it seems filtered?
var style = {
"version": 8,
"sources": {
"cleantech": {
"type": "vector",
// "url": "mapbox://map-id"
// "url": "http://tileserver.com/layer.json",
"tiles": ["http://192.168.1.4:3001/services/postgis/cleantech2/geom/vector-tiles/{z}/{x}/{y}.pbf"],
"maxzoom": 6
}
},
"glyphs": location.origin+location.pathname+"font/{fontstack}/{range}.pbf",
"layers": [{
"id": "cleantech2_geom_id",
"type": "circle",
'source': 'cleantech',
'layout': {
'visibility': 'visible'
},
'paint': {
'circle-radius': 8,
'circle-color': 'rgba(55,148,179,1)'
},
'source-layer': 'cleantech2_geom'
}]
};
edit 2:
change the maxzoom to 22, all data displayed. lets drink!

Resources