Fabricjs line coordinates after (moved, scaled, rotated) - canvas.on('object:modified'… - line

I need to find the Line coordinates(x1,y1,x2,y2) after the object has been modified. (moved, scaled, rotated)
I thought to use the oCoords information and based on angle and flip information to decide which corners are the line ends, but it seems that it will not be too accurate…
Any help?
Example:
x1: 164,
y1: 295.78334045410156,
x2: 451,
y2: 162.78334045410156
x: 163, y: 161.78334045410156 - top left corner
x: 452, y: 161.78334045410156 - top right corner
x: 163, y: 296.78334045410156 - bottom left corner
x: 452, y: 296.78334045410156 - bottom right corner

When Fabric.js calculates oCoords - i.e. object's corners' coordinates - it takes into account the object's strokeWidth:
// fabric.Object.prototype
_getNonTransformedDimensions: function() {
var strokeWidth = this.strokeWidth,
w = this.width + strokeWidth,
h = this.height + strokeWidth;
return { x: w, y: h };
},
For most objects, stroke is kind of a border that outlines the outer edges, so it makes perfect sense to account for strokeWidth it when calculating corner coordinates.
In fabric.Line, though, stroke is used to draw the body of the line. There is no example in the question but I assume this is the reason behind discrepancies between the real end-point coordinates and those in oCoords.
So, if you really want to use oCoords to detect the coordinates of the end points, you'll have to adjust for strokeWidth / 2, e.g.
const realx1 = line.oCoords.tl.x + line.strokeWidth / 2
const realy1 = line.oCoords.tl.y + line.strokeWidth / 2
Keep in mind that fabric.Line's own _getNonTransformedDimensions() does adjust for strokeWidth, but only when the line's width or height equal 0:
// fabric.Line.prototype
_getNonTransformedDimensions: function() {
var dim = this.callSuper('_getNonTransformedDimensions');
if (this.strokeLineCap === 'butt') {
if (this.width === 0) {
dim.y -= this.strokeWidth;
}
if (this.height === 0) {
dim.x -= this.strokeWidth;
}
}
return dim;
},

Related

How to calculate the length in mm of a string in a PDF document created with jsPDF library?

I use jsPDF library to create and print a PDF document. This library exposes low level methods which are ok, but i have tons of fields to create, many of which are similar, and i need to create higher level abstractions.
For example i have a createLabel function that i want to call instead of this low level stuff.
var doc = new jsPDF('portrait', 'mm', 'a4');
doc.addFont('Arial', "sans-serif", "normal");
// name
doc.setFontSize(14);
doc.text(10, 19, "name:");
doc.setLineWidth(0.1);
doc.line(25, 19, 100, 19); // meaning x1, y1, x2, y2
// CUI
doc.setFontSize(14);
doc.text(10, 29, "CUI:");
doc.setLineWidth(0.1);
doc.line(21, 29, 100, 29);
// same stuff but use functions instead.
createLabel("name: ", 10,50, 100); // meaning (labelName, x, y, totalWidth)
createLabel("CUI: ", 10,60, 100);
As you can see, the lines for the second group of labels are not placed in the right position. They are too much on the left. Their starting postion is generated based on the length of the labelName, and this length calculation fails. How can i make this work properly? The code so far is:
function createLabel(name, x, y, totalWidth) {
//draw name
doc.setFontSize(14);
doc.text(x, y, name);
// draw line
const nameLength = (measureLength(name)) + 2;
doc.setLineWidth(0.1);
// i want to start the line after the name ends + 2 mm.
// and end the line in such a way that nameLength + lineLength == totalWidth of the compoenent.
doc.line(x + nameLength, y, x + totalWidth, y);
}
function measureLength(str) {
let canvas = document.createElement('canvas'); // in memory canvas.. not rendered anywere..
let ctx = canvas.getContext("2d")
ctx.font = "14px Arial";
let width = ctx.measureText(str).width;
let mm = ( width * 25.4 ) / 149 // meaning (px * 25.4) / screen DPI
console.log (mm);
return mm; // of course, this calculation turns out wrong..
}
How to make this measureLength function work correctly? Most solutions i found involve DOM but this is PDF.
Notice: I use the same font ('14px Arial') for the PDF document and for the canvas. jsPDF live demo.
Any insight is appreciated, thanks :)
This might resolve your problem:
createLabel(name, x, y, totalWidth) {
doc.setFontSize(14);
doc.text(x, y, name);
// draw line
const nameLength = (doc.getTextDimensions(name).w / (72 / 25.6) ) + 2;
console.log('nameLength', nameLength); // todo remove
doc.setLineWidth(0.1);
// i want to start the line after the name ends + 2 mm.
// and end the line in such a way that nameLength + lineLength == totalWidth of the compoenent.
doc.line(x + nameLength, y, x + totalWidth, y);
}
Check how I calculate nameLength - using build in jsPDF function and converting to mm.
Helpful links:
how to calculate text size
why sometimes calculation might be wrong by few pixels
This is the result:
Remember that you use x + totalWidth for line width, so lines are longer by x compared to manual example at the top

Cytoscape.js layout edge length

Hi I'm trying to build a web-crawler visualization tool for a school project. I decided to use Cytoscape.js and it's been really nice to use. The problem i'm having is the edge lengths for some of the layouts (circle, breadth first, concentric) seem too large and the graph looks odd.
When I first start the application, I manually make and load a graph(it's a tree) with 100 nodes and that looks good in Circle layout:
However after I perform a web-crawl the new graph( this one has 44 nodes) doesn't fit in the view for circle:
Is there a way to get this to work so that the edges are not so long and the nodes look larger?
Edit:
Here's the code I use to change the layout:
changeLayout = function(layoutName, title, root){
var numOfNodes = cy.filter('node').length;
//extent changes when I repeatedly change the layout to circle, (don't understand this behavior)
var extent = cy.extent();
var rect = document.getElementById("cy-container").getBoundingClientRect();
var x1 = rect.left;
var x2 = rect.right;
var y1 = rect.top;
var y2 = rect.bottom;
var height = (y2 - y1);
var width = (x2 - x1);
var fact = (height < width) ? (height/numOfNodes) : (width/numOfNodes);
fact *= 5;
var myRadius = height < width ? (height-fact) : (width-fact);
switch(layoutName){
case 'circle':
myLayout = cy.makeLayout(
{ name: layoutName,
radius: myRadius,
boundingBox: {x1: x1, x2: x2, y1: y1, y2: y2},
fit: true,
avoidOverlap: false
});
break;
case 'concentric':
myLayout = cy.makeLayout(
{ name: layoutName,
height: height,
width: width,
fit: false,
avoidOverlap: true
});
break;
case 'breadthfirst':
myLayout = cy.makeLayout(
{ name: layoutName,
boundingBox: {x1: x1, x2: x2, y1: y1, y2: y2},
fit: true,
roots: root,
avoidOverlap: false
});
break;
default :
myLayout = cy.makeLayout(
{
name: layoutName
});
}
myLayout.run();
$('#graphTitle').text(title + " Layout");
};
The length of an edge is a function of the positions of the nodes. The layout sets the positions, so you have to set the layout options to make the nodes closer together.
For force-directed (physics simulation) layouts, you adjust the forces. In other layouts, you can adjust spacing values or enforce a bounding box to affect how spread out the nodes are.

How do I get the frame of visible content from SKCropNode?

It appears that, in SpriteKit, when I use a mask in a SKCropNode to hide some content, it fails to change the frame calculated by calculateAccumulatedFrame. I'm wondering if there's any way to calculate the visible frame.
A quick example:
import SpriteKit
let par = SKCropNode()
let bigShape = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
bigShape.fillColor = UIColor.redColor()
bigShape.strokeColor = UIColor.clearColor()
par.addChild(bigShape)
let smallShape = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 20, height: 20))
smallShape.fillColor = UIColor.greenColor()
smallShape.strokeColor = UIColor.clearColor()
par.maskNode = smallShape
par.calculateAccumulatedFrame() // returns (x=0, y=0, width=100, height=100)
I expected par.calculateAccumulatedFrame() to return (x=0, y=0, width=20, height=20) based on the crop node mask.
I thought maybe I could code the function myself as an extension that basically reimplements calculateAccumulatedFrame with support for checking for SKCropNodes and their masks, but it occurred to me that I would need to consider the alpha of that mask to determine if there's actual content that grows the frame. Sounds difficult.
Is there an easy way to calculate this?

How interpretation MS-EMF Header object properties - Bounds, Frame, Device and Millimeters?

I'm implement rendering MS-EMF to raster image tool.
Parser by specification work's fine. But i have interpretatioin 2.2.9 Header Object properties when rendering problem, not enough information in the specification.
To convert from LOGICAL to DEVICE coordinates use current MapMode. How to interpret them (especially interesting MM_ISOTROPIC and MM_ANISOTROPIC) can look at gdi, for example here.
Now, i'm trying to specify the position and size of the whole image:
var minPoint = new PointF(header.Bounds.Left, header.Bounds.Top);
var maxPoint = new PointF(header.Bounds.Right, header.Bounds.Bottom);
float imageWidth = maxPoint.X - minPoint.X;
float imageHeight = maxPoint.Y - minPoint.Y;
float shiftX = -minPoint.X;
float shiftY = -minPoint.Y;
var globalCanvas = new CanvasClass(options.PageWidth, options.PageHeight);
globalCanvas.RenderTransform = new DrMatrix(1, 0, 0, 1, 0, 0);
float scaleX = options.PageWidth / (maxPoint.X + shiftX);
float scaleY = options.PageHeight / (maxPoint.Y + shiftY);
float minCommonScale = Math.Min(scaleX, scaleY);
if (minCommonScale > Epsilon)
{
globalCanvas.RenderTransform.Scale(minCommonScale, minCommonScale);
}
globalCanvas.RenderTransform.Translate(shiftX, shiftY);
but i don't understand how to use all properties - Bounds, Frame, Device and Millimeters - and the result image is stretched or not correct scaling or position of the image is not correct.
How them interpret?
Example 1.
emf file
header:
Bounds: (0, 0) - (579, 429)
Frame: (0, 0) - (10000, 10000)
Device: 1855, 1034
Millimeters: 320, 240
and total 4 records:
SelectObject(hDC, (HGDIOBJ)GRAY_BRUSH);
Ellipse(hDC, 0, 0, 99, 99);
SelectObject(hDC, (HGDIOBJ)BLACK_BRUSH);
Ellipse(hDC, 480, 330, 579, 429);
result:
but we must see ex1-ethalon
Interestingly, viewers display ehalon incorrect, except the windows standard viewer:
Example 2.
emf file
header:
Bounds: (960, 210) - (3396, 2429)
Frame: (6772, 1481) - (23969, 17143)
Device: 2892, 4125
Millimeters: 204, 291
result (incomplete rendering yet):
but we see ethalon (attention on the image position):

Mouseevent when near an Element with Raphael

According to this question
Raphael - event when mouse near element
i create a invisible rectangle around another rectangle ,
when the mouse is over that large rect, a circle will appear.
but because the large rect is on top of the small rect,
i can't process another event when mouse is over the small rect.
(if the small rect is on top , the point will disappear when i reach the small rect)
And i want also to have another event with the circle.
Is there any solution for this?
Hier is the code
Kind of mimicking the events of the larger rectangle with the smaller one:
var paper = new Raphael(0, 0, 500, 500);
createRect(100, 100, 100, 50);
function createRect(x, y, width, height) {
var boundrect = paper.rect(x - 30, y - 30, width + 60, height + 60).attr({
"fill": "pink",
"stroke": "none"
}).mouseover(function(event) {
topCtrl.show()
}).mouseout(function(event) {
topCtrl.hide()
})
,
rect = paper.rect(x, y, width, height).attr({
"fill": "white",
"stroke": "red"
}).mouseover(function(event) {
topCtrl.show();
topCtrl.attr({
"fill": "white"
})
}),
topCtrl = paper.circle(x + (width / 2), y, 5).attr({
"fill": "red"
});
}