You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi,
I am opening an issue to provide an example on how to generate a Straight-Skeleton via SVG. It does not contain the actual lines of the straight skeleton, but some tricks on how to generate it as a height map. It includes boundary as well as a hole.
Feel free to use it as a HowTo thi.ng!
Would have liked to try to use geom-isolines to get a vector representation of it, but it seems its better to use hiccup-canvas instead. So there are still possibilities to extend the example. But for the current use, it should work.
SVG Polygons
Polygons with Gradients
Polygons with Darken-Filter
Polygons with Mask - Straight Skeleton
index.ts
import*assvgfrom"@thi.ng/hiccup-svg";import{$compile}from"@thi.ng/rdom";import*asvecfrom"@thi.ng/vectors";/** * end result will be nearly a straight skeleton * that is generated via svg gradients, filters and masks *//** we only create gradients up to a specified max length */constMAX_LENGTH=300;/** * * straight skeleton parts (boundary or hole) * * we will create polygons for each line * they will be extrusions inside to the polygon * all getting a gradient pointing in that same direction * afterwards we will use a darken filter * this way, only the darkest pixel will be shown * and the result will look pretty much like a straight skeleton * * however on concave corners * we have an edge case. * our extrusion wont go around the corner, * but we can use a bisect vector to change the offset * direction of the extrusion. * * * so we offset the line perpendicularly */constcreateStraightSkeletonPart=(path: vec.Vec[])=>{/** * we add the first three points to the end, so we can easily cycle through points * to create bisect vectors for each line */constcyclicPath=[...path, ...path.slice(0,3)];constsvgPolys=path.map((_,i)=>{/** get points for bisecting vectors */constprev=cyclicPath[i];conststart=cyclicPath[i+1];constend=cyclicPath[i+2];constnext=cyclicPath[i+3];constdir=vec.asVec2(vec.normalize([],vec.sub([],end,start)));/** we create bisect vectors at start and end * currently they have a scale of MAX_LENGTH * could be improved by offseting to MAX_LENGTH perpendicular to start-end line * via some trigonometry if we get the correct angle. */constcross=vec.normalize([],[-dir.y,dir.x],MAX_LENGTH);constbisectStart=vec.cornerBisector2(null,prev,start,end,MAX_LENGTH);constbisectEnd=vec.cornerBisector2(null,start,end,next,MAX_LENGTH);/** if our bisect vectors are on concave corners * we will use them to offset the start / end * otherwise we just use the cross vector */constbisectAngleStart=vec.angleBetween2(dir,bisectStart);constbisectAngleEnd=vec.angleBetween2(dir,bisectEnd);conststartOffset=vec.add([],start,bisectAngleStart>Math.PI/2 ? bisectStart : cross);constendOffset=vec.add([],end,bisectAngleEnd<Math.PI/2 ? bisectEnd : cross);/** we need to rotate the final polygon, * since our linear-gradient goes from top to bottom. * we rotate our points so start and end point are in line with [1,0] * afterwards we rotate the polygon back into position * */constradiansAngle=vec.angleBetween2(dir,[1,0],false);constdegreesAngle=(radiansAngle*180)/Math.PI;constrotatedPts=[start,end,endOffset,startOffset,start].map((pt)=>{returnvec.rotateAroundPoint2([],pt,start,radiansAngle);});/** create svg polygon using our gradient * and rotating it back in position */returnsvg.polygon(rotatedPts,{fill: "url(#gradient)",transform: `rotate(${-degreesAngle})`,"transform-origin": `${start.x}${start.y}`,});});returnsvgPolys;};// boundary has winding order clock wise// geojsons standard might be other way around... 🙈constboundary=[[50,50],[400,50],[400,400],[600,600],[400,600],[50,400],].map((v)=>vec.asVec2(v));constboundarySkeletonPart=createStraightSkeletonPart(boundary);// hole has winding order counter clock wiseconsthole=[[200,300],[200,400],[300,400],[300,300],].map((v)=>vec.asVec2(v));constholeSkeletonPart=createStraightSkeletonPart(hole);/** * now we create our svg */$compile(svg.svg({width: "100%",height: "100%",viewBox: "0 0 700 700"},/** * add definition for linear gradient */svg.defs(svg.linearGradient("gradient",[0,0],[0,1],[[0,"black"],[100,"white"],])),/** * add blend mode for polygons */["style",{},"polygon {mix-blend-mode: darken;}"],/** * mask skeleton parts by the boundary */["mask",{id: "mask"},svg.polyline(boundary,{fill: "#fff"})],svg.group({mask: "url(#mask)",},
...boundarySkeletonPart,
...holeSkeletonPart),svg.polyline(boundary,{strokeWidth: 1,stroke: "#f00"}),/** * and now lets fill our hole and we are done */svg.polyline(hole,{strokeWidth: 1,stroke: "#f00",fill: "#fff"}))).mount(document.getElementById("app"));
The text was updated successfully, but these errors were encountered:
@dennemark Sorry for the delay about getting back to you about your straight skeleton. Not forgotten about it. Also, if you're not after extracting the actual geometry of the skeleton, you might find the https://thi.ng/distance-transform package a more simple alternative approach...
🙄thi.ng offers too many algorithms! Super useful!
Distance-transform should be combinable with geom-isolines, I guess.
My approach definitely has some weaknesses, but since I know every line, I can scale the gradient for each of them differently, allowing me to make it weighted. Though I would have to add more gradients in that case.
Hi,
I am opening an issue to provide an example on how to generate a Straight-Skeleton via SVG. It does not contain the actual lines of the straight skeleton, but some tricks on how to generate it as a height map. It includes boundary as well as a hole.
Feel free to use it as a HowTo thi.ng!
Would have liked to try to use geom-isolines to get a vector representation of it, but it seems its better to use hiccup-canvas instead. So there are still possibilities to extend the example. But for the current use, it should work.
SVG Polygons
Polygons with Gradients
Polygons with Darken-Filter
Polygons with Mask - Straight Skeleton
index.ts
The text was updated successfully, but these errors were encountered: