Related
I have 1D pointer array like below:
Darray = [{x: 334, y: 400.5}, {x: 237, y: 389},{x: 149, y: 387.5},{x: 55, y: 379.5},{x: 210, y: 301.5},{x: 48, y: 295.5},{x: 378.5, y: 224.5},{x: 283, y: 217.5},{x: 121.5, y: 211.5},{x: 198.5, y: 211.5},{x: 42.5, y: 201},{x: 33, y: 134},{x: 364, y: 142},{x: 268.5, y: 137},{x: 192, y: 136.5},{x: 106, y: 131.5},{x: 263.5, y: 68},{x: 182.5, y: 63.5},{x: 102.5, y: 61.5},{x: 344.5, y: 65.5},{x: 32, y: 52}]
//console.log(Darray)
const points = Darray.slice();
points.sort((a, b) => a.y - b.y);
console.log(points)
I want to convert this 1D array into 2D array based on the array row length. For example,
row_length = [5, 5, 5, 2, 4]
new_Darray = [[element[1,1],element[1,2], element[1,3], element[1,4], element[1,5],
[element[2,1],element[2,2], element[2,3], element[2,4], element[2,5],
[element[3,1],element[3,2], element[3,3], element[3,4], element[3,5],
[element[4,1],element[4,2],
[element[5,1],element[5,2], element[5,3], element[5,4], element[5,5]]
Here, element[i] represents {x:someValue, y:someValue}
Sorry, if I wrongly represented anything. Any help will be highly appreciated.
Using map, slice and temp variable, can be simplified to one-liner
const Darray = [
{ x: 334, y: 400.5 },
{ x: 237, y: 389 },
{ x: 149, y: 387.5 },
{ x: 55, y: 379.5 },
{ x: 210, y: 301.5 },
{ x: 48, y: 295.5 },
{ x: 378.5, y: 224.5 },
{ x: 283, y: 217.5 },
{ x: 121.5, y: 211.5 },
{ x: 198.5, y: 211.5 },
{ x: 42.5, y: 201 },
{ x: 33, y: 134 },
{ x: 364, y: 142 },
{ x: 268.5, y: 137 },
{ x: 192, y: 136.5 },
{ x: 106, y: 131.5 },
{ x: 263.5, y: 68 },
{ x: 182.5, y: 63.5 },
{ x: 102.5, y: 61.5 },
{ x: 344.5, y: 65.5 },
{ x: 32, y: 52 },
];
const points = Darray.slice();
points.sort((a, b) => a.y - b.y);
// console.log(points);
const row_length = [5, 5, 5, 2, 4];
let last = 0;
const output = row_length.map((len) => points.slice(last, (last += len)));
console.log(output);
Hello I'm having some trouble with drawing 3 lines on a canvas relatively to my mouse click.
The 3 lines make a small smiley face.
So in my code I have a function called parseRecordedLinePoints(), In this function it makes the points from each line relative to my mouse, then strokes it on the canvas.
For some reason when it's called and strokes the canvas, the eyes get stroked above each other. (overlaps)
I've tried to fix it by changing the function to:
function parseRecordedLinePoints(Point, index, arr) {
ctx.lineTo(Point.x, Point.y);
ctx.stroke();
}
When the function is like this it draws the smiley, but not relatively and the lines connect to each other.
Here's the code currently as an snippet, so you can get a better look. You can see how it draws the eyes in the wrong position, and overlaps them directly under my mouse. I'm trying to make it draw the smiley face correctly relatively.
CODE:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var cursorX = 100; //Example position X
var cursorY = 0; //Example position Y
var relX = 0;
var relY = 0;
//3 lines // A smiley face.
var recordedLines = [{
Color: {
r: 0,
g: 50,
b: 0
},
LinePoints: [{
x: 125,
y: 370.2
},
{
x: 125,
y: 367.9
},
{
x: 127.2,
y: 365.7
},
{
x: 128.4,
y: 367.9
},
{
x: 127.2,
y: 370.2
},
{
x: 125,
y: 371.3
},
{
x: 122.8,
y: 370.2
},
{
x: 121.7,
y: 367.9
},
{
x: 121.7,
y: 364.6
},
{
x: 123.9,
y: 363.5
},
{
x: 127.2,
y: 363.5
},
{
x: 128.4,
y: 365.7
},
{
x: 128.4,
y: 369
},
{
x: 126.1,
y: 370.2
},
{
x: 123.9,
y: 369
},
{
x: 122.8,
y: 366.8
},
{
x: 123.9,
y: 364.6
},
{
x: 127.2,
y: 364.6
},
{
x: 127.2,
y: 367.9
},
{
x: 125,
y: 369
},
{
x: 123.9,
y: 366.8
},
{
x: 123.9,
y: 366.8
}
]
},
{
Color: {
r: 100,
g: 0,
b: 0
},
LinePoints: [{
x: 168.4,
y: 365.7
},
{
x: 166.1,
y: 366.8
},
{
x: 163.9,
y: 363.5
},
{
x: 166.1,
y: 362.4
},
{
x: 168.4,
y: 363.5
},
{
x: 167.2,
y: 365.7
},
{
x: 163.9,
y: 365.7
},
{
x: 163.9,
y: 362.4
},
{
x: 165,
y: 360.2
},
{
x: 168.4,
y: 360.2
},
{
x: 169.5,
y: 363.5
},
{
x: 168.4,
y: 365.7
},
{
x: 165,
y: 365.7
},
{
x: 163.9,
y: 363.5
},
{
x: 165,
y: 360.2
},
{
x: 168.4,
y: 360.2
},
{
x: 168.4,
y: 363.5
},
{
x: 168.4,
y: 364.6
}
]
},
{
Color: {
r: 100,
g: 0,
b: 0
},
LinePoints: [{
x: 107.2,
y: 377.9
},
{
x: 109.5,
y: 379
},
{
x: 110.6,
y: 381.3
},
{
x: 112.8,
y: 382.4
},
{
x: 115,
y: 384.6
},
{
x: 117.2,
y: 386.8
},
{
x: 119.5,
y: 387.9
},
{
x: 122.8,
y: 389
},
{
x: 126.1,
y: 390.2
},
{
x: 129.5,
y: 391.3
},
{
x: 132.8,
y: 391.3
},
{
x: 136.1,
y: 391.3
},
{
x: 139.5,
y: 392.4
},
{
x: 142.8,
y: 392.4
},
{
x: 146.1,
y: 392.4
},
{
x: 149.5,
y: 392.4
},
{
x: 152.8,
y: 392.4
},
{
x: 156.1,
y: 391.3
},
{
x: 159.5,
y: 390.2
},
{
x: 161.7,
y: 389
},
{
x: 165,
y: 389
},
{
x: 167.2,
y: 387.9
},
{
x: 169.5,
y: 386.8
},
{
x: 171.7,
y: 385.7
},
{
x: 173.9,
y: 384.6
},
{
x: 176.1,
y: 383.5
},
{
x: 178.4,
y: 382.4
},
{
x: 179.5,
y: 380.2
},
{
x: 181.7,
y: 379
},
{
x: 182.8,
y: 376.8
},
{
x: 183.9,
y: 375.7
}
]
}
];
//----------------------------------
//var relativeARRAY = []; //recordedLines array, but relative to mouse.
//var color = [{ r: 0, g: 0, b: 0 }];
function parseRecordedLinePoints(Point, index, arr) {
relX = Point.x - arr[0].x;
relY = Point.y - arr[0].y;
ctx.lineTo(relX + cursorX, relY + cursorY);
ctx.stroke();
}
//----------------------------------
function parseRecordedLine(Line, index, arr) {
const LinePoints = Line.LinePoints;
//-- parse the x and y points of this line.. to make relative..
LinePoints.forEach(parseRecordedLinePoints);
}
//----------------------------------
c.addEventListener(
"click",
function(e) {
cursorX = e.clientX;
cursorY = e.clientY;
ctx.moveTo(cursorX, cursorY);
recordedLines.forEach(parseRecordedLine);
},
false
);
<canvas id="myCanvas" width="600" height="600" style="border: 1px solid #d3d3d3;"></canvas>
Not sure if you have to use all those points for what you’re working on but here’s a simplified version.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
let mouse = {
x: null,
y: null,
}
window.addEventListener('click', function(e) {
mouse.x = e.x;
mouse.y = e.y;
drawFace();
});
function drawFace() {
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(mouse.x, mouse.y, 5, 0, Math.PI * 2);
ctx.arc(mouse.x + 30, mouse.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(mouse.x + 14, mouse.y + 9, 30, 0.3, Math.PI / 1.1)
ctx.stroke();
ctx.closePath();
}
For your code you need to use moveTo when starting a new draw. lineTo will continue drawing from the previous point. moveTo picks up the pen and moves it to a new start location.
I am using LightningChart JS and would like to implement a virtual measurement device, where I can click on point A and then drag to point B and obtain the x,y values of both point A and point B.
As far as I have looked into the event handlers they just return a mouse event with start and stop positions in terms of screen positions. Please, correct me if I'm wrong. Also please suggest an efficient way to do this.
Thanks in advance.
The mouse events return the mouse coordinates in the same coordinate space as normal JS mouse events. To get the click location in the series coordinate space, a couple of steps need to be taken.
First the mouse coordinates need to be converted to the engine coordinate space. The engine coordinate space is the canvas area with 0,0 on the bottom left of the canvas. This can be done with chart.engine.clientLocation2Engine(ev.clientX,ev.clientY). This returns the event coordinate in the engine coordinate space using the chart engine scale.
This needs to be then converted to the series coordinate. This can be done with translatePoint method. translatePoint can be used to convert points between two different scales. Scale in LightningChart JS is basically a coordinate space.
const m = chart.engine.clientLocation2Engine(ev.clientX, ev.clientY)
const translated = translatePoint(m, chart.engine.scale, lineSeries.scale)
Now the translated variable contains the click location in the series coordinate space.
See a full code snippet below where you can drag on the series area and when drag is stopped markers are placed to the start and end locations of the drag.
const {
lightningChart,
SolidLine,
SolidFill,
ColorRGBA,
AxisTickStrategies,
UIOrigins,
DataPatterns,
translatePoint,
ColorHEX
} = lcjs
const chart = lightningChart().ChartXY()
const diesel = [
{ x: 0, y: 1.52 },
{ x: 1, y: 1.52 },
{ x: 2, y: 1.52 },
{ x: 3, y: 1.58 },
{ x: 4, y: 2.00 },
{ x: 5, y: 2.00 },
{ x: 6, y: 2.00 },
{ x: 7, y: 2.00 },
{ x: 8, y: 2.26 },
{ x: 9, y: 1.90 },
{ x: 10, y: 1.90 },
{ x: 11, y: 1.90 },
{ x: 12, y: 1.90 },
{ x: 13, y: 1.60 },
{ x: 14, y: 1.60 },
{ x: 15, y: 1.60 },
{ x: 16, y: 1.00 },
{ x: 17, y: 1.00 },
{ x: 18, y: 1.00 },
{ x: 19, y: 1.74 },
{ x: 20, y: 1.47 },
{ x: 21, y: 1.47 },
{ x: 22, y: 1.47 },
{ x: 23, y: 1.74 },
{ x: 24, y: 1.74 },
{ x: 25, y: 1.74 },
{ x: 27, y: 1.5 },
{ x: 28, y: 1.5 },
{ x: 29, y: 1.5 }
]
const gasoline = [
{ x: 0, y: 1.35 },
{ x: 1, y: 1.35 },
{ x: 2, y: 1.35 },
{ x: 3, y: 1.35 },
{ x: 4, y: 1.90 },
{ x: 5, y: 1.90 },
{ x: 6, y: 1.90 },
{ x: 7, y: 1.92 },
{ x: 8, y: 1.50 },
{ x: 9, y: 1.50 },
{ x: 10, y: 1.3 },
{ x: 11, y: 1.3 },
{ x: 12, y: 1.3 },
{ x: 13, y: 1.3 },
{ x: 14, y: 1.3 },
{ x: 15, y: 1.32 },
{ x: 16, y: 1.40 },
{ x: 17, y: 1.44 },
{ x: 18, y: 1.02 },
{ x: 19, y: 1.02 },
{ x: 20, y: 1.02 },
{ x: 21, y: 1.02 },
{ x: 22, y: 1.02 },
{ x: 23, y: 1.02 },
{ x: 24, y: 1.02 },
{ x: 25, y: 1.02 },
{ x: 27, y: 1.30 },
{ x: 28, y: 1.30 },
{ x: 29, y: 1.30 }
]
const lineSeries = chart.addLineSeries({ dataPattern: DataPatterns.horizontalProgressive })
const lineSeries2 = chart.addLineSeries({ dataPattern: DataPatterns.horizontalProgressive })
lineSeries2.add(diesel.map((point) => ({ x: point.x, y: point.y })))
lineSeries.add(gasoline.map((point) => ({ x: point.x, y: point.y })))
const markerA = chart.addChartMarkerXY()
.setPointMarker((marker) => marker.setFillStyle((f => f.setColor(ColorHEX('#f00')))))
.setMouseInteractions(false)
const markerB = chart.addChartMarkerXY()
.setPointMarker((marker) => marker.setFillStyle((f => f.setColor(ColorHEX('#0f0')))))
.setMouseInteractions(false)
function getClickInSeriesScale(point, scale) {
const m = chart.engine.clientLocation2Engine(point.x, point.y)
return translatePoint(m, chart.engine.scale, scale)
}
chart.onSeriesBackgroundMouseDragStop((obj, ev, b, start) => {
let pointA = getClickInSeriesScale(start, lineSeries.scale)
let pointB = getClickInSeriesScale({x:ev.clientX,y:ev.clientY}, lineSeries.scale)
// move markes to start and end points
markerA.setPosition(pointA)
markerB.setPosition(pointB)
})
<script src="https://unpkg.com/#arction/lcjs#1.3.1/dist/lcjs.iife.js"></script>
I have a dataset like this:
const dataset = [
{ 'color': 'red', 'data': [{ x: 0, y: 600 }, { x: 2, y: 900 }, { x: 4, y: 650 }, { x: 6, y: 700 }, { x: 9, y: 600 }] },
{ 'color': 'blue', 'data': [{ x: 0, y: 400 }, { x: 2, y: 300 }, { x: 4, y: 450 }, { x: 6, y: 900 }, { x: 9, y: 400 }] },
{ 'color': 'yellow', 'data': [{ x: 0, y: 200 }, { x: 2, y: 100 }, { x: 4, y: 550 }, { x: 6, y: 600 }, { x: 9, y: 400 }] }
];
I want to find domain values for max and min x-axis. I try this code but it doesn't work:
.domain([d3.min(arrangedata, (array) => array.x), d3.max(arrangedata, (array) => array.x)])
I also tried d3.extend, but I couldn't handle it. Any idea ?
For using either d3.max/min or d3.extent, you have to merge the inner arrays, which you can do with Array.prototype.reduce. Then, specify the x property in the accessor.
All together, it's just this:
const extent = d3.extent(dataset.reduce((a, c) => a.concat(c.data), []), d => d.x);
Here is the demo:
const dataset = [{
'color': 'red',
'data': [{
x: 0,
y: 600
}, {
x: 2,
y: 900
}, {
x: 4,
y: 650
}, {
x: 6,
y: 700
}, {
x: 9,
y: 600
}]
},
{
'color': 'blue',
'data': [{
x: 0,
y: 400
}, {
x: 2,
y: 300
}, {
x: 4,
y: 450
}, {
x: 6,
y: 900
}, {
x: 9,
y: 400
}]
},
{
'color': 'yellow',
'data': [{
x: 0,
y: 200
}, {
x: 2,
y: 100
}, {
x: 4,
y: 550
}, {
x: 6,
y: 600
}, {
x: 9,
y: 400
}]
}
];
const extent = d3.extent(dataset.reduce((a, c) => a.concat(c.data), []), d => d.x);
console.log(extent)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
You can try applying Math. min/max to the mapped series data.
d3.minValue = function(dataset, field) {
return d3.min(dataset, series => Math.min.apply(Math, series.data.map(data => data[field])))
}
d3.maxValue = function(dataset, field) {
return d3.max(dataset, series => Math.max.apply(Math, series.data.map(data => data[field])))
}
const dataset = [{
'color': 'red',
'data': [{ x: 0, y: 600 }, { x: 2, y: 900 }, { x: 4, y: 650 }, { x: 6, y: 700 }, { x: 9, y: 600 }]
}, {
'color': 'blue',
'data': [{ x: 0, y: 400 }, { x: 2, y: 300 }, { x: 4, y: 450 }, { x: 6, y: 900 }, { x: 9, y: 400 }]
}, {
'color': 'yellow',
'data': [{ x: 0, y: 200 }, { x: 2, y: 100 }, { x: 4, y: 550 }, { x: 6, y: 600 }, { x: 9, y: 400 }]
}]
//let domain = d3.domain([ d3.minValue(dataset, 'x'), d3.maxValue(dataset, 'x') ])
console.log(d3.minValue(dataset, 'x'), d3.maxValue(dataset, 'x')) // 0, 9
console.log(d3.minValue(dataset, 'y'), d3.maxValue(dataset, 'y')) // 100, 900
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Here is my chart:
Here's is the chart if I add a line as the first series (note the lack of labels on the x axis):
For some reason, most of the points on the x axis are disappearing.
Here's a reduced case:
$(function () {
$('#container').highcharts({
xAxis: {
type: 'datetime'
},
series: [
{
type: 'line',
data: [
{ x: 1476749571000, y: 36581540.37153554 },
{ x: 1477429910000, y: 36763124.20193958 }
]
},
{
type: 'scatter',
data: [
{ x: 1476749571000, y: 37170874 },
{ x: 1476756736000, y: 36541961 },
{ x: 1476760298000, y: 36412560 },
{ x: 1476771158000, y: 36766829 },
{ x: 1476774759000, y: 36819204 },
{ x: 1476778351000, y: 36193069 },
{ x: 1476781953000, y: 36289990 },
{ x: 1476789144000, y: 36751180 },
{ x: 1476796338000, y: 36058553 },
{ x: 1476807129000, y: 36803593 },
{ x: 1476810763000, y: 35963538 },
{ x: 1476821507000, y: 36896693 },
{ x: 1476825189000, y: 36187712 },
{ x: 1476835902000, y: 37084673 },
{ x: 1476843121000, y: 37136577 },
{ x: 1476846735000, y: 36607531 },
{ x: 1476853934000, y: 36334610 },
{ x: 1476864716000, y: 37140192 },
{ x: 1476875538000, y: 35929136 },
{ x: 1476900734000, y: 36137618 },
{ x: 1476907888000, y: 37195952 },
{ x: 1476915103000, y: 37170028 },
{ x: 1476922333000, y: 36438640 },
{ x: 1476936739000, y: 36425736 },
{ x: 1476951130000, y: 36954993 },
{ x: 1476969138000, y: 36288051 },
{ x: 1476972690000, y: 36666854 },
{ x: 1476976353000, y: 37066932 },
{ x: 1476979924000, y: 36628386 },
{ x: 1476990726000, y: 36997524 },
{ x: 1477005114000, y: 37036777 },
{ x: 1477008734000, y: 36069816 },
{ x: 1477019586000, y: 36788879 },
{ x: 1477026712000, y: 37195266 },
{ x: 1477030349000, y: 36568077 },
{ x: 1477033944000, y: 35936353 },
{ x: 1477041102000, y: 35840667 },
{ x: 1477069967000, y: 36826033 },
{ x: 1477077134000, y: 37165725 },
{ x: 1477084331000, y: 37078309 },
{ x: 1477091521000, y: 37195750 },
{ x: 1477095153000, y: 36788377 },
{ x: 1477105972000, y: 36951953 },
{ x: 1477109539000, y: 37072255 },
{ x: 1477113124000, y: 35688693 },
{ x: 1477120378000, y: 37021539 },
{ x: 1477134712000, y: 36883044 },
{ x: 1477138302000, y: 36838067 },
{ x: 1477141938000, y: 36719537 },
{ x: 1477145571000, y: 35900066 },
{ x: 1477149173000, y: 36141158 },
{ x: 1477156353000, y: 36647699 },
{ x: 1477163535000, y: 36516520 },
{ x: 1477167130000, y: 35817224 },
{ x: 1477181544000, y: 36758274 },
{ x: 1477188754000, y: 36717498 },
{ x: 1477195935000, y: 36913901 },
{ x: 1477203152000, y: 36518492 },
{ x: 1477217518000, y: 37143381 },
{ x: 1477224743000, y: 36334856 },
{ x: 1477231914000, y: 36910162 },
{ x: 1477249968000, y: 36977544 },
{ x: 1477257157000, y: 37174847 },
{ x: 1477267956000, y: 37051864 },
{ x: 1477289539000, y: 36343981 },
{ x: 1477296761000, y: 37020845 },
{ x: 1477300357000, y: 36180803 },
{ x: 1477307506000, y: 36145191 },
{ x: 1477311156000, y: 37102437 },
{ x: 1477325543000, y: 35496804 },
{ x: 1477332732000, y: 37193777 },
{ x: 1477339942000, y: 36683704 },
{ x: 1477354323000, y: 36577391 },
{ x: 1477357932000, y: 36231277 },
{ x: 1477361539000, y: 37198585 },
{ x: 1477365133000, y: 36789070 },
{ x: 1477379517000, y: 37138966 },
{ x: 1477386738000, y: 36953176 },
{ x: 1477397539000, y: 37135591 },
{ x: 1477408304000, y: 35867866 },
{ x: 1477411936000, y: 37141581 },
{ x: 1477415513000, y: 36864017 },
{ x: 1477422756000, y: 36743302 },
{ x: 1477426328000, y: 36948156 },
{ x: 1477429910000, y: 37199122 }
]
}
]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
Any ideas?
There appears to be a difference in how Highcarts interprets things between the line and scatter series types, though I am unclear how the particular scenario is explained.
But you can work around this by using a line series for both, and simply removing the line from the scatter data.
This way, you can skip setting a tickInterval, and you get the dynamic interpretation by the chart that you want.
Example:
{
type: 'line',
lineWidth: 0,
marker: {
enabled: true
},
findNearestPointBy: 'xy',
data: [ ... ]
}
Updated fiddle:
http://jsfiddle.net/jlbriggs/g6fjjach/4/
Set axis.tickInterval to force ticks to be drawn.
xAxis: {
type: 'datetime',
tickInterval: 1000 * 3600 * 24 // one day interval
},
example: http://jsfiddle.net/g6fjjach/2/