Hello everyone. How can I make this polyline with 90-degree and external borders? I tried to make it with clockwise. But I can not.
Thanks
Hi @yaseminV2XL5 ,
You probably want to use an alpha shape algorithm
Hello @yaseminV2XL5 - Just curious, do you have the grey surface already? If so, that should be easy to pull with the Surface.PerimeterCurves node, if it’s a PolySurface made of multiple surfaces, you can then Thicken it, and intersect with a Plane to get the combined surface, and then pull the PerimeterCurves.
Hallo @solamour .Yes, I have the grey surface. But I don’t want areas that are triangular.
Ah gotcha - Here is one way to do it
- Get Perimeter curves of original surface
- Run a Python based Delaunay algorithm that uses Numpy and Scipy
- FIlter out all diagonal lines, leaving only vertical and horizontal
- Use TSplines to build surfaces from these individual, unorganized pipes
This doesn’t automate the process though, and requires you to tweak on two places - the Delaunay alpha threshold, and the TSpline maxFaceValence.
YaseminV2XL5_AlphaShape_to_remove_Diagonal_Lines.dyn (50.4 KB)
Hi @solamour ,
Nice solution!! If you don’t mind, I’d like to sharpen your explanation a bit
A delaunay triangulation is a completely separate thing from an alpha shape algorithm.
A Delaunay triangulation, as it says, triangulates points in such a way that no points fall within the circumcircle of three points making up a triangle. This would then ideally result in a triangulation with ‘nicer’ triangles, meaning not long/ stretched.
The alpha shape algorithm uses the threshold you’re talking about. This would be the radius of the search circle that you’re using to cut away the shape. I always think of it like a foam shape where the points are rocks that cannot be cut away. Having a really big alpha shape threshold therefore actually results in the convex hull.
It’s amazing. It works very well. Thank you very much @solamour for your answer and for your time. It shows how important the methods in geometry are.
Thanks @Daan for your answer
You are most welcome @yaseminV2XL5 Glad it works for you!
Here is an alternative implementation in Python, a brute force approach nonetheless, returning the boundary that minimizes the ratio between perimeter and enclosed area.
import clr
import random
from typing import Iterable
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
points = [
Point.ByCoordinates(1, 0),
Point.ByCoordinates(2, 0),
Point.ByCoordinates(2, 1),
Point.ByCoordinates(3, 1),
Point.ByCoordinates(3, 2),
Point.ByCoordinates(3, 3),
Point.ByCoordinates(2, 3),
Point.ByCoordinates(2, 2),
Point.ByCoordinates(1, 2),
Point.ByCoordinates(0, 2),
Point.ByCoordinates(0, 1),
Point.ByCoordinates(1, 1)
]
def sort(data: Iterable[Point], i: int = 0) -> Iterable[Point]:
current = data.pop(i)
ret = [current]
xaxis = Vector.ByCoordinates(1, 0).Normalized()
vec = xaxis
length = 0
while len(data) > 0:
closest = {}
for p in data:
d = p.DistanceTo(current)
closest.setdefault(d, []).append(p)
q = min(closest)
candidates = closest[q]
if len(candidates) > 1:
target = sorted(candidates, key=lambda k: Vector.ByTwoPoints(current, k).Dot(vec) * vec.Dot(xaxis))[0]
else:
target = candidates[0]
data.pop(data.index(target))
vec = Vector.ByTwoPoints(current, target)
length += vec.Length
current = target
ret.append(current)
return ret, length
def main(data: Iterable[Point]) -> PolyCurve:
pts = [p for p in data]
random.shuffle(pts)
solutions = {}
for i in range(len(pts)):
sequence, length = sort([p for p in pts], i)
c = Polygon.ByPoints(sequence)
if len(c.SelfIntersections()) > 0:
continue
s = 0
try:
s = c.Patch().Area
except:
continue
if s == 0:
continue
solutions.setdefault(length / s, []).append(sequence)
length = min(k for k in solutions)
candidates = solutions[length][0]
return length, candidates, PolyCurve.ByPoints(candidates, True)
OUT = main(points)