SVG intersection of elements with transforms - svg

The Intersection library from Kevin Lindsey allows to easily compute intersections between several types of svg elements (paths, rectangles, lines...).
However, it does not seem to take into account the transformations applied via transform="translate(x,y) rotate(r)" to any of the objects.
<svg>
<polygon id="machine" transform="translate(40,25) rotate(45)"... />
<path id="outer" ... />
</svg>
<script>
var machine = document.getElementById("machine")
var outer = document.getElementById("outer")
var m = new Polygon(machine)
var o = new Path(outer)
Intersection.intersectPathShape(o, m) // incorrect
</script>
Is there a way to take the transformations applied to any element into account while computing intersections?

You could try changing the values of to their 'screen' values, extracting the transforms. Below are some examples for polygons and paths(except arcs):
Note on paths: Chrome deprecated pathSegList recently and now requires a polyfill,
https://github.com/progers/pathseg
//---except arc paths---
function screenPath(path)
{
var sCTM = path.getCTM()
var svgRoot = path.ownerSVGElement
var segList=path.pathSegList
var segs=segList.numberOfItems
//---change segObj values
for(var k=0;k<segs;k++)
{
var segObj=segList.getItem(k)
if(segObj.x && segObj.y )
{
var mySVGPoint = svgRoot.createSVGPoint();
mySVGPoint.x = segObj.x
mySVGPoint.y = segObj.y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
segObj.x=mySVGPointTrans.x
segObj.y=mySVGPointTrans.y
}
if(segObj.x1 && segObj.y1)
{
var mySVGPoint1 = svgRoot.createSVGPoint();
mySVGPoint1.x = segObj.x1
mySVGPoint1.y = segObj.y1
mySVGPointTrans1 = mySVGPoint1.matrixTransform(sCTM)
segObj.x1=mySVGPointTrans1.x
segObj.y1=mySVGPointTrans1.y
}
if(segObj.x2 && segObj.y2)
{
var mySVGPoint2 = svgRoot.createSVGPoint();
mySVGPoint2.x = segObj.x2
mySVGPoint2.y = segObj.y2
mySVGPointTrans2 = mySVGPoint2.matrixTransform(sCTM)
segObj.x2=mySVGPointTrans2.x
segObj.y2=mySVGPointTrans2.y
}
}
//---force removal of transform--
path.setAttribute("transform","")
path.removeAttribute("transform")
}
//---changes all transformed points to screen points---
function screenPolygon(myPoly)
{
var sCTM = myPoly.getCTM()
var svgRoot = myPoly.ownerSVGElement
var pointsList = myPoly.points;
var n = pointsList.numberOfItems;
for(var m=0;m<n;m++)
{
var mySVGPoint = svgRoot.createSVGPoint();
mySVGPoint.x = pointsList.getItem(m).x
mySVGPoint.y = pointsList.getItem(m).y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
pointsList.getItem(m).x=mySVGPointTrans.x
pointsList.getItem(m).y=mySVGPointTrans.y
}
//---force removal of transform--
myPoly.setAttribute("transform","")
myPoly.removeAttribute("transform")
}

Related

Add dimension between link and element

i am tried to find the link wall face,but when i use the reference to create a new dimension , i will get result about 'invaild number of references'. i cant trans link face to active face.
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
var rf1 = uidoc.Selection.PickObject(ObjectType.PointOnElement, "select");
var element1 = doc.GetElement(rf1);
var location = element1.Location as LocationPoint;
var point = location.Point;
var rf2 = uidoc.Selection.PickObject(ObjectType.LinkedElement, "select");
var linkElement = doc.GetElement(rf2) as RevitLinkInstance;
var linkDoc = linkElement.GetLinkDocument();
var linkWall = linkDoc.GetElement(rf2.LinkedElementId) as Wall;
var wallLocation = linkWall.Location as LocationCurve;
var curve = wallLocation.Curve;
var cRf = curve.Reference;
var solid = BIMTools.Geometry.GetSolid(linkWall);
Face face = null;
foreach (var solidFace in solid.Faces)
{
XYZ normal = ((Face)solidFace).ComputeNormal(new UV(0, 0));
if (normal.Y < 0)
{
face = solidFace as Face;
break;
}
}
var viewLevel = uidoc.ActiveView.GenLevel.Elevation;
var tPoint = new XYZ(point.X,(face as PlanarFace).Origin.Y, viewLevel);
point = new XYZ(point.X, point.Y, viewLevel);
var line = Line.CreateBound(point, tPoint);
var references = new ReferenceArray();
references.Append(rf1);
references.Append(face.Reference);
using (Transaction trans = new Transaction(doc,"create"))
{
trans.Start();
var dimension = doc.Create.NewDimension(uidoc.ActiveView, line, references);
trans.Commit();
}
return Result.Succeeded;
}
The Building Coder provides a whole list of articles on creating dimensioning.

ifelse Declaration or statement expected

function resp (){
if (distance<0);{
var para = document.createElement("p");
var node = document.createTextNode("Yes");
para.appendChild(node);
var element = document.getElementById("inda");
element.appendChild(para);}
else{
var para = document.createElement("p");
var node = document.createTextNode("No, time left:");
para.appendChild(node);
var element = document.getElementById("inda");
element.appendChild(para);}
}
Why does it give me the error: Declaration or statement expected
if (distance<0);{
-->
if (distance<0) {
It's easier to find such problem if you have an ide like webstorm or visual studio code.
Your code in pretty printed form:
function resp() {
if (distance < 0) {
var para = document.createElement("p");
var node = document.createTextNode("Yes");
para.appendChild(node);
var element = document.getElementById("inda");
element.appendChild(para);
} else {
var para = document.createElement("p");
var node = document.createTextNode("No, time left:");
para.appendChild(node);
var element = document.getElementById("inda");
element.appendChild(para);
}
}
Your code after little refactoring:
function resp() {
const label = (distance < 0) ? 'Yes' : 'No, time left:';
var para = document.createElement("p");
var node = document.createTextNode(label);
para.appendChild(node);
var element = document.getElementById("inda");
element.appendChild(para);
}

How to get individual object's width, height, left and top in selection:created event?

I am getting the values of the properties of an object(top,width...) as an object is being scaled/moved by using this function :
canvas.on('object:scaling', onObjectModification);
canvas.on('object:moving', onObjectModification);
canvas.on('object:rotating', onObjectModification);
function onObjectModification(e) {
var activeObject = e.target;
var reachedLimit = false;
objectLeft = activeObject.left,
objectTop = activeObject.top,
objectWidth = activeObject.width,
objectHeight = activeObject.height,
canvasWidth = canvas.width,
canvasHeight = canvas.height;
}
How can I get the same for each object that are being moved as a group? I need the values to be constantly changing as the object:scaling event provide.
I know of the event selection:created but I am lost how to use that to attain what I want. Any help from you guys would be highly appreciated.
Thanks
during object scaling width and height will not change. They will be the same all the time, just scaleX and scaleY will change.
You have to write a function that will iterate on possibile group sub objects.
canvas.on('object:scaling', onObjectModification);
canvas.on('object:moving', onObjectModification);
canvas.on('object:rotating', onObjectModification);
function onObjectModification(e) {
var activeObject = e.target;
if (!activeObject) {
return;
}
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var reachedLimit = false;
var objectLeft = activeObject.left;
var objectTop = activeObject.top;
// this provide scaled height and width
var objectWidth = activeObject.getWidth();
var objectHeight = activeObject.getHeight();
if (activeObject._objects) {
objs = activeObject._objects;
for (var i= 0; i < objs.length; i++) {
var obj = objs[i];
var objWidth = activeObject.getWidth() * activeObject.scaleX;
var objHeight = activeObject.getHeight() * activeObject.scaleY;
}
}
}

Get New path after applying the translate rotate and scale on arc SVG

I want to get new Path (Arc) point after applying the translate rotate and scale on the path .
I am Putting an example:
MY Path is shown like This
AS You seen Translate ,rotate and scale is applied on my path.
I am using this code for getting new path with translate(0,0) rotate(0,0,0) and scale(0,0) I am using matrixTransform(sCTM) method to get new point in the case of line after applying new transform and scale.
But in the case of arc i am not getting the right path.
var path = d3.select("g")[0][0]; // Get g tag
var d = "";
// console.log(path.select('path').getAttr('d'));
var sCTM = path.getCTM(); // get transform data
var svgRoot = path.ownerSVGElement // get svg tag
var pathId = d3.select("g path")[0][0]; // get path
var segList = pathId.pathSegList //
var segs = segList.numberOfItems
//---change segObj values
// create D after apply transform elements
for (var k = 0; k < segs; k++) {
var segObj = segList.getItem(k)
if (segObj.x && segObj.y) {
var mySVGPoint = svgRoot.createSVGPoint();
mySVGPoint.x = segObj.x
mySVGPoint.y = segObj.y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
console.log("testing my object",mySVGPointTrans);
segObj.x = mySVGPointTrans.x;
segObj.y = mySVGPointTrans.y;
d = d + segObj.pathSegTypeAsLetter + mySVGPointTrans.x + "," + mySVGPointTrans.y;
}
}
d3..select("g").select("path").attr("d",d);
//---force removal of transform--
path.setAttribute("transform", "translate(0,0)rotate(0,0,0)skewX(0)scale(1,1)");
IN the case of line the Matrix return 2 value x point and y point .
but in the case of arc the matrix retuen 6 value cx cy rotation
swepflag largefalg x y. in this code first i check path is arc or line
if line then add the x,y coordinate only but in the case of arc i add
all 6 point to the make new path arc path.
var path = d3..select("g")[0][0]; // Get g tag
var d = "";
// console.log(path.select('path').getAttr('d'));
var sCTM = path.getCTM(); // get transform data
var svgRoot = path.ownerSVGElement // get svg tag
var pathId = d3.select("g path")[0][0]; // get path
var segList = pathId.pathSegList //
var segs = segList.numberOfItems
//---change segObj values
// create D after apply transform elements
for (var k = 0; k < segs; k++) {
var segObj = segList.getItem(k)
if(segObj.pathSegTypeAsLetter != "A" && segObj.pathSegTypeAsLetter !="a") {``
if (segObj.x && segObj.y) {
var mySVGPoint = svgRoot.createSVGPoint();
mySVGPoint.x = segObj.x
mySVGPoint.y = segObj.y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
console.log("testing my object",mySVGPointTrans);
segObj.x = mySVGPointTrans.x;
segObj.y = mySVGPointTrans.y;
d = d + segObj.pathSegTypeAsLetter + mySVGPointTrans.x + "," + mySVGPointTrans.y;
}
} else {
// to check the arc have rotation value of x and y
if (segObj.x && segObj.y && segObj.r1 && segObj.r2) {
var mySVGPoint = svgRoot.createSVGPoint();
mySVGPoint.x = segObj.x
mySVGPoint.y = segObj.y
mySVGPoint.r1 = segObj.r1
mySVGPoint.r2 = segObj.r2
mySVGPoint.angle = segObj.angle
mySVGPoint.largeArcFlag = segObj.largeArcFlag
mySVGPoint.sweepFlag = segObj.sweepFlag;
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
segObj.x = mySVGPointTrans.x
segObj.y = mySVGPointTrans.y
var largeFlag = segObj.largeArcFlag ? 1 : 0 ;
var sweepFlag = segObj.sweepFlag ? 0 : 1;
//add all parameter when arc
d = d + segObj.pathSegTypeAsLetter + segObj.r1 + "," + segObj.r2 + " " + segObj.angle + " " + largeFlag + " " + sweepFlag + " " + mySVGPointTrans.x + "," + mySVGPointTrans.y;
}
}
}
d3..select("g").select("path").attr("d",d);
//---force removal of transform--
path.setAttribute("transform", "translate(0,0)rotate(0,0,0)skewX(0)scale(1,1)");

How can I remove all SVG elements which do not intersect a particular rectangle?

I have an SVG image from which I'd like to extract several rectangular regions as independent SVG images. Because the original image is rather large but the elements intersecting the areas of interest are small, I do not want elements which lie entirely outside the cropped viewBox to remain in the cropped SVG image.
Conceptually, what I'm looking for is this:
The cropped SVG (yes, the output must be SVG, not a bitmap) should not contain elements for the spiral or the star, as they fall entirely outside the area of interest. (Yes, the nonintersecting elements really must be removed, due to the source SVG being several orders of magnitude more bytes than the cropped SVG, as I intend to crop the source in different ways hundreds of times.) I want to be able to specify the area of interest on the command-line, as well, due to having quite a number of such cropped images to produce.
Are there any tools which can do this?
SVG elements can be parsed and flagged true/false as inside a svg rect object and/or intersecting the rect object.
Would this work for you?
isEnclosed = mySVG.checkEnclosure(myElement, RectObj)
doesIntersect = mySVG.checkIntersection(myElement, RectObj)
I use the Jordan Curve Theorem to test for points inside an svg polygon. Your polygon would be your viewBox. Posssibly this could work for you if each element has a target point associated with it(i.e. center point) to determine if you want to show it or not.
The following is the javascript I use:(caution: I think the polygon should have counter-clockwise points. Always a good idea when dealing with polygons)
//---Point-in-polygon: Jordan Curve Theorem---
function pointInPolygon(myPolygon,px,py)
{
var pointsList=myPolygon.points
var x
var y
var x1
var y1
var crossings=0
var verts=pointsList.numberOfItems
//---Iterate through each line ---
for ( var i = 0; i < verts; i++ )
{
var vertx=pointsList.getItem(i).x
var verty=pointsList.getItem(i).y
if(i<verts-1)
{
var vertxNext=pointsList.getItem(i+1).x
var vertyNext=pointsList.getItem(i+1).y
}
else
{
var vertxNext=pointsList.getItem(0).x
var vertyNext=pointsList.getItem(0).y
}
/* This is done to ensure that we get the same result when
the line goes from left to right and right to left */
if ( vertx < vertxNext){
x1 = vertx;
x2 = vertxNext;
} else {
x1 = vertxNext;
x2 = vertx;
}
/* First check if the ray is possible to cross the line */
if ( px > x1 && px <= x2 && ( py < verty || py <= vertyNext ) ) {
var eps = 0.000000001;
/* Calculate the equation of the line */
var dx = vertxNext - vertx;
var dy = vertyNext - verty;
var k;
if ( Math.abs(dx) < eps ){
k = Infinity;
} else {
k = dy/dx;
}
var m = verty - k * vertx;
/* Find if the ray crosses the line */
y2 = k * px + m;
if ( py <= y2 ){
crossings++;
}
}
}
//---odd number of crossings: point inside polygon--
var crossFlag=(crossings/2)+""
if(crossFlag.indexOf(".")!=-1)
return true;
else
return false;
}
Since your svg elements are transformed, you may need to convert them to screen points. I've used the following javascript for the various svg elements(line, rect, circle, ellipse, polygon, polyline, and path)
//----build a generic document SVG root to hold svg point---
function screenLine(line,svg)
{
var sCTM = line.getCTM()
var x1=parseFloat(line.getAttribute("x1"))
var y1=parseFloat(line.getAttribute("y1"))
var x2=parseFloat(line.getAttribute("x2"))
var y2=parseFloat(line.getAttribute("y2"))
var mySVGPoint1 = svg.createSVGPoint();
mySVGPoint1.x = x1
mySVGPoint1.y = y1
mySVGPointTrans1 = mySVGPoint1.matrixTransform(sCTM)
line.setAttribute("x1",mySVGPointTrans1.x)
line.setAttribute("y1",mySVGPointTrans1.y)
var mySVGPoint2 = svg.createSVGPoint();
mySVGPoint2.x = x2
mySVGPoint2.y = y2
mySVGPointTrans2= mySVGPoint2.matrixTransform(sCTM)
line.setAttribute("x2",mySVGPointTrans2.x)
line.setAttribute("y2",mySVGPointTrans2.y)
//---force removal of transform--
line.setAttribute("transform","")
line.removeAttribute("transform")
}
function screenCircle(circle,svg)
{
var sCTM = circle.getCTM()
var scaleX = sCTM.a;
var cx=parseFloat(circle.getAttribute("cx"))
var cy=parseFloat(circle.getAttribute("cy"))
var r=parseFloat(circle.getAttribute("r"))
var mySVGPointC = svg.createSVGPoint();
mySVGPointC.x = cx
mySVGPointC.y = cy
mySVGPointTransC = mySVGPointC.matrixTransform(sCTM)
circle.setAttribute("cx",mySVGPointTransC.x)
circle.setAttribute("cy",mySVGPointTransC.y)
circle.setAttribute("r",r*scaleX)
//---force removal of transform--
circle.setAttribute("transform","")
circle.removeAttribute("transform")
}
function screenEllipse(ellipse,svg)
{
var sCTM = ellipse.getCTM()
var scaleX = sCTM.a;
var scaleY = sCTM.d;
var cx=parseFloat(ellipse.getAttribute("cx"))
var cy=parseFloat(ellipse.getAttribute("cy"))
var rx=parseFloat(ellipse.getAttribute("rx"))
var ry=parseFloat(ellipse.getAttribute("ry"))
var mySVGPointC = svg.createSVGPoint();
mySVGPointC.x = cx
mySVGPointC.y = cy
mySVGPointTransC = mySVGPointC.matrixTransform(sCTM)
ellipse.setAttribute("cx",mySVGPointTransC.x)
ellipse.setAttribute("cy",mySVGPointTransC.y)
ellipse.setAttribute("rx",rx*scaleX)
ellipse.setAttribute("ry",ry*scaleY)
//---force removal of transform--
ellipse.setAttribute("transform","")
ellipse.removeAttribute("transform")
}
function screenRect(rect,svg)
{
var sCTM = rect.getCTM()
var scaleX = sCTM.a;
var scaleY = sCTM.d;
var x=parseFloat(rect.getAttribute("x"))
var y=parseFloat(rect.getAttribute("y"))
var width=parseFloat(rect.getAttribute("width"))
var height=parseFloat(rect.getAttribute("height"))
var mySVGPoint = svg.createSVGPoint();
mySVGPoint.x = x
mySVGPoint.y = y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
rect.setAttribute("x",mySVGPointTrans.x)
rect.setAttribute("y",mySVGPointTrans.y)
rect.setAttribute("width",width*scaleX)
rect.setAttribute("height",height*scaleY)
//---force removal of transform--
rect.setAttribute("transform","")
rect.removeAttribute("transform")
}
function screenPolyline(myPoly,svg)
{
var sCTM = myPoly.getCTM()
var pointsList = myPoly.points;
var n = pointsList.numberOfItems;
for(var m=0;m<n;m++)
{
var mySVGPoint = mySVG.createSVGPoint();
mySVGPoint.x = pointsList.getItem(m).x
mySVGPoint.y = pointsList.getItem(m).y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
pointsList.getItem(m).x=mySVGPointTrans.x
pointsList.getItem(m).y=mySVGPointTrans.y
}
//---force removal of transform--
myPoly.setAttribute("transform","")
myPoly.removeAttribute("transform")
}
function screenPath(path,svg)
{
var sCTM = path.getCTM()
var scaleX = sCTM.a;
var scaleY = sCTM.d;
var segList=path.pathSegList
var segs=segList.numberOfItems
//---change segObj values
for(var k=0;k<segs;k++)
{
var segObj=segList.getItem(k)
if(segObj.x && segObj.y )
{
var mySVGPoint = svg.createSVGPoint();
mySVGPoint.x = segObj.x
mySVGPoint.y = segObj.y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
segObj.x=mySVGPointTrans.x
segObj.y=mySVGPointTrans.y
}
if(segObj.x1 && segObj.y1)
{
var mySVGPoint1 = svg.createSVGPoint();
mySVGPoint1.x = segObj.x1
mySVGPoint1.y = segObj.y1
mySVGPointTrans1 = mySVGPoint1.matrixTransform(sCTM)
segObj.x1=mySVGPointTrans1.x
segObj.y1=mySVGPointTrans1.y
}
if(segObj.x2 && segObj.y2)
{
var mySVGPoint2 = svg.createSVGPoint();
mySVGPoint2.x = segObj.x2
mySVGPoint2.y = segObj.y2
mySVGPointTrans2 = mySVGPoint2.matrixTransform(sCTM)
segObj.x2=mySVGPointTrans2.x
segObj.y2=mySVGPointTrans2.y
}
if(segObj.r1)segObj.r1=segObj.r1*scaleX
if(segObj.r2)segObj.r2=segObj.r2*scaleX
}
//---force removal of transform--
path.setAttribute("transform","")
path.removeAttribute("transform")
}
//---changes all transformed points to screen points---
function screenPolygon(myPoly,mySVG)
{
var sCTM = myPoly.getCTM()
var pointsList = myPoly.points;
var n = pointsList.numberOfItems;
for(var m=0;m<n;m++)
{
var mySVGPoint = mySVG.createSVGPoint();
mySVGPoint.x = pointsList.getItem(m).x
mySVGPoint.y = pointsList.getItem(m).y
mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
pointsList.getItem(m).x=mySVGPointTrans.x
pointsList.getItem(m).y=mySVGPointTrans.y
}
//---force removal of transform--
myPoly.setAttribute("transform","")
myPoly.removeAttribute("transform")
}

Resources