d3.symbols being cut off by ClipPath area - symbols

Still in the process of improving my competence about D3, I got stuck with a problem where I'm trying to plot a zoomable curve in a SVG element with margins (so that I need a clipPath rect to avoid that plot invades margins when zoomed) but the clipPath margins cut the display of d3.symbols off the plot.
This is the relevant code for the plot
var margin = {top: 20, right: 60, bottom: 30, left: 30},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", h);
// The curve I want to plot: y=x^2
var my_curve = d3.range(10).map(function(d) { var my_y = d * d; return { "x" : d, "y" : my_y }; });
var x_range_min = d3.min(my_curve, function(d) { return d.x; });
var x_range_max = d3.max(my_curve, function(d) { return d.x; });
var y_range_min = d3.min(my_curve, function(d) { return d.y; });
var y_range_max = d3.max(my_curve, function(d) { return d.y; });
var xScale = d3.scaleLinear().domain([x_range_min, x_range_max]).range([0, w]);
var yScale = d3.scaleLinear().domain([y_range_max, y_range_min]).range([0, h]);
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
// symbols
svg.selectAll(".my_pts").data(my_curve).enter().append("path")
.attr("class", "my_pts")
.attr("d", d3.symbol().type(d3.symbolTriangle).size(200))
.attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
// with this zoomed line does not enter margin area
.attr("clip-path", "url(#clip)");
...as you can see only part of the triangle symbol is depicted, I guess because the path is drawn at 0,0 and cut by the clipPath before the translation can be performed.
I have also posted this fiddle https://jsfiddle.net/fabio_p/988c1sjv/
where you can find a more complete version of the code, with the brush & zoom function, so that you can see why the clipPath is needed (if you have never encountered the issue with margins before)
My question is the following: is there a workaround to this problem? I was hoping to find a way to directly draw the symbol in the right place without the need of a later translation (possibly with a "custom symbol type"), but it seems it goes beyond my current skills, since I was unable to produce any actual solution.
Any suggestion would be welcome. Thanks in advance.

Create a group with the clip path:
var clipping = svg.append("g")
.attr("clip-path", "url(#clip)");
And append both the line and the symbols to the group:
clipping.append("path")
.data([my_curve])
//etc...
Here is your updated fiddle: https://jsfiddle.net/cvtvrL2q/

Related

How to move point's label and SVGs by moving a point graphic in highcharts gantt?

In this example, I tried to move the points by points.graphic.translate(0, -25), but it can't help to move point's label and SVGs. You can see the details in the example.
events: {
load() {
var chart = this,
series = chart.series[0];
series.points.forEach(function(point) {
point.graphic.translate(0, -25);
});
}
}
You need to move each element separately.
Label:
point.dataLabel.text.translate(0, -25)
And custom image just after rendering it:
points.forEach(function(point) {
point.customImg = chartt.renderer.image(
'https://www.highcharts.com/images/employees2014/Torstein.jpg',
point.plotX + chartt.plotLeft + point.shapeArgs.width / 2 - width / 2,
point.plotY + chartt.plotTop - height / 2,
width,
height
)
.attr({
zIndex: 5
})
.add();
point.customImg.translate(0, -25)
});
Demo: https://jsfiddle.net/BlackLabel/05hmufxa/

KonvaJS crop and resize

I'm using KonvaJS to create a simple Image-Editor. As a base for the image resize function I used the code from the Konva Examples (Knonva JS Image Resize). But now I'm struggling with the implementation of a crop function. On a button click, I enable the user to draw a rectangle on the stage. I then use the built-in crop function on my image with the coordinates, width and height of the drawn rectangle and crop the image. But when I resize the image before I crop it the cropped area shows the part cropped from the original sized image.
Is there an easy, build in way I'm missing to be able to crop from the resized image? Or do I have to calculate the position and size of the drawn rectangle according to the resized values of the image, then crop that part and resize and reposition the result?
Crop Image:
function cropImage(x, y, width, height, activeLayer) {
var image = activeLayer.get('Image')[0], xDiff = 0, yDiff = 0, newWidth, newHeight, newX, newY;
// only Crop visible Parts of the Image
if(x < activeLayer.getX()) {
xDiff = activeLayer.getX() - x;
}
if(y < activeLayer.getY()) {
yDiff = activeLayer.getY() - y;
}
if (x + width > activeLayer.getX() + activeLayer.width()) {
width = width - ((x + width) - (activeLayer.getX() + activeLayer.width()));
}
if (y + height > activeLayer.getY() + activeLayer.height()) {
height = height - ((y + height) - (activeLayer.getY() + activeLayer.height()));
}
newHeight = height - yDiff;
newWidth = width - xDiff;
newX = (x - activeLayer.getX()) + image.cropX() + xDiff;
newY = (y - activeLayer.getY()) + image.cropY() + yDiff;
image.width(newWidth);
image.height(newHeight);
activeLayer.width(newWidth);
activeLayer.height(newHeight);
activeLayer.setX(newX + activeLayer.getX() - image.cropX());
activeLayer.setY(newY + activeLayer.getY() - image.cropY());
image.crop({
x : newX ,
y : newY ,
width : newWidth,
height : newHeight
});
//Reposition anchors so topLeft Anchor is always in 0/0 of the grouplayer
repositionAnchors(activeLayer);
activeLayer.draw();
}
This is not yet a precise answer to your question but I am including here because it might show the way for you or others in the same territory of cropping.
Run the snippet full screen.
Left image is Konva, right is the original image.
Click-drag on the left image to make a selection. The left image changes to be the selection only whilst the right shows the position of the crop. Repeat the process to see how the crops build up.
I have NOT scaled the image on the canvas in this example, so it is not an exact answer to your needs at present, but useful for visualising what is happening. I can add scaling if folks think it would be useful.
// Useful frequently used variables.
var sX = 0, sY = 0, sW = 400, sH = 200; // drawing dimensions
var iW = 0, iH = 0; // image dimensions
var cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
var imgRect = $('.imgRect');
var imgPtr = $('#imgPtr');
var scale = 1;
var img = $('#daImg');
var src = "https://dummyimage.com/400x200/e85de8/fff&text=SO Rocks!"
$('.container').css({width: sW, height: sH});
// Vars for mouse rect work.
var posStart, posNow, mode = '';
// Set up add a stage & layer
var s1 = new Konva.Stage({container: 'container', width: sW, height: sH});
var l1 = new Konva.Layer({});
s1.add(l1);
var image = new Konva.Image({}) // prepare an image to display the picture.
l1.add(image);
// I use a foreground rect to catch events - this covers the konva image completely - you can wire your events in your own way
var r1 = new Konva.Rect({x: 0, y: 0, width: sW, height: sH, fill: 'gold', opacity: 0 })
l1.add(r1)
// draw a rectangle to be used as the rubber-band area
var r2 = new Konva.Rect({x: 0, y: 0, width: 0, height: 0, stroke: 'red', dash: [2,2]})
r2.listening(false); // stop r2 catching our mouse events otherwise if we reverse mouse direction events may not fire
l1.add(r2)
// Mouse movement funcs
function startDrag(posIn){
posStart = {x: posIn.x, y: posIn.y};
posNow = {x: posIn.x, y: posIn.y};
}
// update rubber rect position
function updateDrag(posIn){
posNow = {x: posIn.x, y: posIn.y};
var posRect = reverse(posStart,posNow);
r2.x(posRect.x1);
r2.y(posRect.y1);
r2.width(posRect.x2 - posRect.x1);
r2.height(posRect.y2 - posRect.y1);
r2.visible(true);
s1.draw(); // redraw any changes.
sayRect(r2);
showImgRect(r2);
}
// start the rubber rect drawing on mouse down.
r1.on('mousedown', function(e){
mode = 'drawing';
startDrag({x: e.evt.layerX, y: e.evt.layerY})
})
// update the rubber rect on mouse move - note use of 'mode' var to avoid drawing after mouse released.
r1.on('mousemove', function(e){
if (mode === 'drawing'){
updateDrag({x: e.evt.layerX, y: e.evt.layerY})
}
showImgPtr(e.evt.layerX, e.evt.layerY);
sayPos(e.evt.layerX, e.evt.layerY);
})
// When user releases the mouse we note the size and modify the clip rect.
r1.on('mouseup', function(e){
mode = '';
r2.visible(false);
// leave a rect to show the target
imgRect.hide();
var imgRect2 = imgRect.clone();
imgRect2
.appendTo('#container2')
.addClass('deleteMe')
.show();
setCrop(r2);
sayInfo(img, image);
})
// Draw a rect on the original image to show location and size. Just using some simple jquery to manipulate a div.
function showImgRect(r){
imgRect.css({
left: r.x() + cropRect.x,
top: r.y() + cropRect.y,
width: r.width() * 1,
height: r.height() * 1
})
imgRect.show();
}
// show a mouse pointer on the original image so we get a sense of what is going on
function showImgPtr(x, y){
imgPtr.css({ left: cropRect.x + x, top: cropRect.y + y})
}
// Set the new crop rect, taking account of previous crops
function setCrop(r){
image.cropX(r.x() + cropRect.x);
image.cropY(r.y() + cropRect.y);
image.cropWidth(r.width() * scale);
image.cropHeight(r.height() * scale);
image.width(r.width());
image.height(r.height());
l1.draw();
cropRect = {x: cropRect.x + r.x(), y: cropRect.y + r.y(), width: r.width(), height: r.height()};
}
// This event listener is fired when the image is loaded - could be a few secs delay for a big image
// so this is effectively an async technique.
img.on('load', function() {
// note the dimensions
iW = img.width();
iH = img.height();
// set the konva image details
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.image(img[0]);
sayInfo(img, image);
l1.draw(); // redraw the layer to see what happened
});
// This innocent looking line intiates the image load and ultimately fires the event above.
img.prop('src', src);
/*
From here down is utility stuff
*/
// Say something useful
function sayInfo(img, image){
$('#imgInfo').html("HTML Image size " + img.width() + " x " + img.height());
$('#imageInfo').html("Konva.image " + image.x() + ", " + image.y() + " - " + image.width() + " x " + image.height());
var info = $('#info');
}
function sayRect(r){
var rectInfo = $('#rectInfo');
rectInfo.html("Clip rect on canvas " + r.x() + ", " + r.y() + " - " + r.width() + " x " + r.height());
}
function sayPos(x, y){
var posInfo = $('#posInfo');
posInfo.html("Pos on stage " + x + ", " + y);
}
// This is just to reverse co-ords if user drags left / up
function reverse(r1, r2){
var r1x = r1.x, r1y = r1.y, r2x = r2.x, r2y = r2.y, d;
if (r1x > r2x ){
d = Math.abs(r1x - r2x);
r1x = r2x; r2x = r1x + d;
}
if (r1y > r2y ){
d = Math.abs(r1y - r2y);
r1y = r2y; r2y = r1y + d;
}
return ({x1: r1x, y1: r1y, x2: r2x, y2: r2y}); // return the corrected rect.
}
// reset function
function reset(){
sX = 0; sY = 0; sW = 400; sH = 300; // drawing dimensions
iW = 0; iH = 0; // image dimensions
iW = img.width();
iH = img.height();
cropRect = {x: sX, y: sY, width: iW, height: iH}; // scaled rect
scale = 1;
if (image){
console.log('iH=' +iH);
image.x(sX);
image.y(sY);
image.width(iW);
image.height(iH);
image.cropX(sX);
image.cropY(sY);
image.cropWidth(iW);
image.cropHeight(iH);
}
$('.deleteMe').remove();
$('.imgRect').hide();
l1.draw();
}
$('#reset').on('click', function(){reset()});
p
{
padding: 5px;
}
.container {
position: relative;
display: inline-block;
width: 500px;
height: 400px;
background-color: transparent;
overflow: hidden;
border: 1px solid silver;
}
.imgRect {
position: absolute;
border: 1px dotted red;
background-color: Aqua;
opacity: 0.3;
}
#imgPtr {
position: absolute;
background-color: red;
width: 1px;
height: 1px;
border-radius: 50%;
border: 2px solid red;
}
a {
color: red;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>
<span id='text'>How crop rect relates to original image. First image is the Konva stage, second is the original image. Use click and drag to draw successive rects on the Konva image.</span> <a id='reset'>Reset</a>
</p>
<p>
<span id='imgInfo'></span><br />
<span id='imageInfo'></span>
<span id='rectInfo'>Rect info </span><br/>
<span id='posInfo'>Pos on stage </span><br/>
<span id='scaleInfo'>Scale 1:1 </span>
</p>
<div id='container' class='container'></div>
<div id='container2' class='container'>
<img id='daImg' />
<div class='imgRect'></div>
<div id='imgPtr'></div>
</div>
When resizing i preserve the ratio for width and height in an array and calculate the position and dimension of the rectangle which is drawn for the crop :
cropX = ((x / ratios[id].width) - (activeLayer.getX() / ratios[id].width)) + (image.cropX()) + (xDiff / ratios[id].width);
cropY = ((y / ratios[id].height)- (activeLayer.getY() / ratios[id].height)) + (image.cropY()) + (yDiff / ratios[id].height);
cropWidth = (width / ratios[id].width) - (xDiff / ratios[id].width),
cropHeight = (height / ratios[id].height) - (yDiff / ratios[id].height)

Highcharts - spider web chart questions

I'm working with Highcharts spider web chart at the moment and wanted to see if I can do the following
How do I zoom polar charts? (or is it possible?)
How do I put background-color in each of the segment?
How do I zoom polar charts? (or is it possible?)
If you want a zoom like zoomType then no. zoomType is disabled for polar charts by highcharts-more.js. From source:
// Disable certain features on angular and polar axes
chart.inverted = false;
chartOptions.chart.zoomType = null;
How do I put background-color in each of the segment?
You can use math and Chart.renderer to create and fill paths to color the background of the segments. For example you might do it like this:
var colors = [ "pink", "yellow", "blue", "red", "green", "cyan", "teal", "indigo" ];
var parts = 6;
for(var i = 0; i < parts; i++) {
centerX = chart.plotLeft + chart.yAxis[0].center[0];
centerY = chart.plotTop + chart.yAxis[0].center[1];
axisLength = chart.yAxis[0].height;
angleOffset = -Math.PI/2;
angleSegment = Math.PI/(parts/2);
firstPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * i)));
firstPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * i)));
secondPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * (i+1))));
secondPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * (i+1))));
chart.renderer.path([
'M', centerX, centerY,
'L', firstPointX, firstPointY,
'L', secondPointX, secondPointY,
'Z'
]).attr({
fill: colors[i % colors.length],
'stroke-width': 1,
'opacity': 1
}).add();
}
As seen in this JSFiddle demonstration. You just have to match number of categories with the parts variable.

Photoshop script: How to find distance of grapic center related to the upper left corner of canvas?

My problem is this:
Is it possible to measure with Photoshop script (I use CS5.1) THE EXACT (x,y) of the center of the graphic (as shown in the image), related to the upper left corner of the canvas (0,0)? What is the tactic I should follow? Anyone has an idea? (The graphic is in its own layer, and I want to do the measure for each graphic, layer by layer, in order to form the layout in Corona).
Yes, in Photoshop, click on "Image" in the navigation menu, then choose Image Size. Take the width and divide by 2, take the height and divide by 2.
To find the coordinates of the centre of the image you need to find the layer bounds, which will tell you the left, top, right and bottom values of the image. From this we can work out the width and height of the image and the centre (from the top left of the photoshop image)
//pref pixels
app.preferences.rulerUnits = Units.PIXELS;
// call the source document
var srcDoc = app.activeDocument;
// get current width values
var W = srcDoc.width.value;
var H = srcDoc.height.value;
var X = srcDoc.activeLayer.bounds[0]
var Y = srcDoc.activeLayer.bounds[1]
var X1 = srcDoc.activeLayer.bounds[2]
var Y1 = srcDoc.activeLayer.bounds[3]
var selW = parseFloat((X1-X));
var selH = parseFloat((Y1-Y));
var posX = Math.floor(parseFloat((X+X1)/2));
var posY = Math.floor(parseFloat((Y+Y1)/2));
alert(X + ", " + Y + ", " + X1 + ", " + Y1 + "\n" + "W: " + selW + ", H: " + selH + "\nPosition " + posX + "," + posY);

Highcharts tooltip always on right side of cursor

I want to show the tooltip on the right side of the cursor.
I looked in the documentation/examples but I can't find a way to force the tooltips to stay on the right side of the cursor.
Can anyone tell me how to do it?
With tooltip positioner I only can set a default position.
Tooltip positioner is much more than just default position. The function arguments contain info about your point position & tooltip dimensions, using which it should be fairly simple to position it to the right.
Highchart/stock allows you to define your alternate positioner as follows
tooltip:{
positioner:function(boxWidth, boxHeight, point){
...
}
}
Note that you have three arguments (boxWidth, boxHeight, point) at your disposal, these seem to be sufficient for most of the use cases to calculate a desired tooltip position. boxWidth and boxHeight are the width and height that your tooltip will require, hence you can use them for edge cases to adjust your tooltip and prevent it from spilling out of the chart or even worse getting clipped.
The default tooltip positioner that comes with highstock is as follows (Source)
/**
* Place the tooltip in a chart without spilling over
* and not covering the point it self.
*/
getPosition: function (boxWidth, boxHeight, point) {
// Set up the variables
var chart = this.chart,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
distance = pick(this.options.distance, 12), // You can use a number directly here, as you may not be able to use pick, as its an internal highchart function
pointX = point.plotX,
pointY = point.plotY,
x = pointX + plotLeft + (chart.inverted ? distance : -boxWidth - distance),
y = pointY - boxHeight + plotTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip
alignedRight;
// It is too far to the left, adjust it
if (x < 7) {
x = plotLeft + pointX + distance;
}
// Test to see if the tooltip is too far to the right,
// if it is, move it back to be inside and then up to not cover the point.
if ((x + boxWidth) > (plotLeft + plotWidth)) {
x -= (x + boxWidth) - (plotLeft + plotWidth);
y = pointY - boxHeight + plotTop - distance;
alignedRight = true;
}
// If it is now above the plot area, align it to the top of the plot area
if (y < plotTop + 5) {
y = plotTop + 5;
// If the tooltip is still covering the point, move it below instead
if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
y = pointY + plotTop + distance; // below
}
}
// Now if the tooltip is below the chart, move it up. It's better to cover the
// point than to disappear outside the chart. #834.
if (y + boxHeight > plotTop + plotHeight) {
y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below
}
return {x: x, y: y};
}
With all the above information, I think you have sufficient tools to implement your requirement by simply modifying the function to make float to right instead of the default left.
I will go ahead and give you the simplest implementation of positioning tooltip to right, you should be able to implement the edge cases based on the aftermentioned default tooltip positioner's code
tooltip: {
positioner: function(boxWidth, boxHeight, point) {
return {x:point.plotX + 20,y:point.plotY};
}
}
Read more # Customizing Highcharts - Tooltip positioning
The better solution to get your tooltip always on the right side of the cursor is the following:
function (labelWidth, labelHeight, point) {
return {
x: point.plotX + labelWidth / 2 + 20,
y: point.plotY + labelHeight / 2
};
}

Resources