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();
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,
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 }
}}
/>
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.
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!