# Point on poly with chord

## Recommended Posts

Hello everyone

I'm looking for a way or node like "point on poly".
However, the point on the polyline should be defined by the chord and not the distance along the guideline.

I know there is not a simple built in way to do this. But I am not certain how I would approach this if it was my problem.

It is simple to get a visual representation of the intersection by drawing a circle from the first point with a radius equal to the chord length you want.

The hard part is actually getting the intersection as none of the intersect routines in the Graphical Calculation section of the VS Function Reference appear to work with Polylines.

Maybe convert a copy of the polyline to Lines, which should give you a group of lines.  Then check each line for intersection with the circle?

@Sam Jones We had a conversation 20 years ago about Chords. Any help here?

And a call to the regular VS warriors @MullinRJ @JBenghiat @michaelk @Jesse Cogswell

No help here.  I haven't a clue.  I'll go out on a limb and say VS is not up to it, but would be delighted to be shown I was wrong.  I started listing a bunch of issues here, but it got crazy.  Why do you want to have this functionality?  How would you like to use it?  @MullinRJ have you got a magic vector math solution?

Without a built-in function, this is a very complicated problem. Polylines can be drawn many different ways, with a mixture of control point types. Calculating Line intersections with straight and arc segments is relatively straight forward, but intersections with bezier and cubic sections are much more complicated and require iterative solutions.

At one time I started trying to solve this problem but only got so far and could not model every combination of control points that can make up a poly line. In that effort there were several hundred lines of code – and I never finished. Without a firm understanding of Cubic Spline algebra and the ways VW implements a subset of it, I would not suggest one stroll too far down this path. If you do, bring plenty of bread crumbs. You may need them to find your way home.

If approximate results are good enough, perhaps Pat's suggestion of converting the Polyline to Lines, or a Polygon, first, might be the way to go. Beware, I converted a 9 point Polyline to a Polygon and got 68 vertices on a 2D Conversion Resolution of Low, and 1804 vertices with a 2D Conversion Resolution of Very High. The Polyline conversion at Low Resolution may have a manageable number of segments, but the accuracy may not be acceptable. Whereas the higher resolutions may provide the accuracy, but the vertex count will be in the hundreds, if not thousands, and computation time will be noticeably slow. Remember you not only  have to find the intersection of two lines, one pair at a time, but you also have to determine of the intersection point is on the line segment.

Sorry I couldn't be of greater assistance.

********  STOP THE PRESS!   ********

All of that said — take a look at the IntersectSurface() function. This is not one I've played with before, but on a whim I just tried the menu command and it could be quite useful, if you can devise a meaningful way to harvest your results. In Vectorscript the command is:

H := IntersectSurface(LineHand, PolyHand);

You will get multiple line segments as the Polyline will dice the line into pieces. There are scenarios where all endpoints of the resultant lines land on the poly (even number of intersections), and some where the last point does not touch  the poly, but it is needed to preserve the last line segment (odd number of intersections). This command is not the total answer and will require some extra code to be useful. I'll leave that to others. It's past my bed time, so I'm going to call it quits here.

Good luck,

Raymond

My reading of the question is that @SimA wants to define the chord length and then calculate where that chord length next contacts the polyline.

So drawing the circle at the first point is relatively easy.  And comparing even a few thousand lines to intersect the circle should not be too bad as it can be solved in linear time not exponential time since you don't need to compare every line to each other.

And maybe add the bounding box check we discussed in the other thread so you don't have to actually test objects that dont' have any chance of being the chord.

But a better description of what is actually being done and why might let us generate better options.

Good morning, @Pat Stanford.

If the path object is a Polygon, I think I've solved that before. Let me look and I'll get back...

Raymond

... The problem I solved was similar, but not the same. My solution assumed the chord length was less than the line/poly segments, where this solution must assume the chord length can be longer or shorter than the line/poly segments.

Other questions need to be answered. Since this is supposed to be a Marionette node, what constraints exist for the inputs, and what format is the output? Whatever the answers, this is not going  to be an easy solution.

Raymond

Hello everyone

I plan and project kitchens, shop fittings, bakeries, counters and much more. Until recently, these were mostly cubic and angular. Organic forms are now often required.

The furniture usually has fixed axis lengths or standard widths.
"Point on Poly" works more or less well because the chord is too short because the length is calculated along the polyline. Now when it comes to execution, I have to determine the intersection points with circles.
This procedure takes a lot of time because several drawings often have to be made.

I have attached my file with the current marionette.
This certainly explains it better than my Google translated one.

Another thought: a curved line is always longer than a direct straight line connecting two points.

I tried using a loop in the code to move the point on the line until it was at the intersection of the chord. The shifting was done, for example, in tenths of the difference between chord and nDist up to an accuracy of 0.000000x.
The problem was that the point was calculated from the polyline starting point. However, I would need a continuous calculation from point A to B to C etc.
Unfortunately, I failed because of my ignorance and GPT.

Thank you for all your ideas!

Hello @SimA,

Are all of your chords the same length or will they differ as you go down the line (um, curve?)

Raymond

Hallo @MullinRJ

Most are different.

Vectorworks doesn’t have this intersection function in its API. To truly solve this mathematically, you would need to use the control points and arc types of each polyline segment, then intersect with a circle, but that is probably overkill.

I can think of two solutions:

Use PointAlongPoly(). The length will be >= your chord length, so start with that as an initial value, and increase your length by 1mm until the distance from the start point to the end point >= your chord length.
For subsequent points, set your start point to the found point, and the start length to the found length + chord length.

Call Polygonize() on the original polyline with a segment length of 1mm, then iterate over the new polygon’s points, developing a list of start and and points whose distance equals your chord length. I believe this is essentially what pointalongpoly is doing under the hood.

Obviously, you can change 1mm to wherever your tolerance is.

And if you really wanted to automate things, you would have a list of stock sizes and a constant representing the maximum angle between segments. You would try successively smaller chord lengths from your list until you either were under your angle tolerance or reached the end of the list.

The chord is now calculated from p1. However, processing the list would also have to use a sequence of consecutive points.

How do I have to incorporate this into the code?

import vs
import math
import Marionette

@Marionette.NodeDefinition
class Params(metaclass=Marionette.OrderedClass):
# APPEARANCE
# Name
this = Marionette.Node("Get Point on Poly and Distance")
this.SetDescription('Returns a point at the specified distance along the poly starting from the first vertex of the poly, and calculates the distance between two points')

# Input Ports
poly = Marionette.PortIn(vs.Handle(0), 'hPoly')
poly.SetDescription("The input poly")
nDist = Marionette.PortIn(0, 'nDist')
nDist.SetDescription("The specified distance")
p1 = Marionette.PortIn((0, 0), 'p1')
p1.SetDescription("A 2D point representing the start point")
nSehne = Marionette.PortIn(0, 'nSehne')
nSehne.SetDescription("A specified length representing the radius of a circle")

# OIP Controls
tolerance = Marionette.OIPControl('Tolerance', Marionette.WidgetType.Text, '0.0000001')
tolerance.SetDescription("An acceptable difference between the calculated and approximate values")

# Output Ports
p2 = Marionette.PortOut('p2')
p2.SetDescription("The result point")
vTan = Marionette.PortOut('vTan')
vTan.SetDescription("The vector tangent to the poly at the result point")
b = Marionette.PortOut('b')
b.SetDescription("True if the node completes its job successfully, false otherwise")

# BEHAVIOR

def RunNode(self):
# inputs
poly = self.Params.poly.value
dist = self.Params.nDist.value
tolerance = self.Params.tolerance.value
p1 = self.Params.p1.value
nSehne = self.Params.nSehne.value

# script
result = vs.PointAlongPolyN(poly, dist, tolerance)
p2 = result[1]
vTan = result[2]

# Calculate distance between p1 and p2
c = Marionette.TupleMap(lambda x, y: (y - x) ** 2, p1, p2)
l = math.sqrt(sum(c))

# If the distance between p1 and p2 is greater than or equal to nSehne, extend the distance until it reaches nSehne
while l < nSehne:
dist += 1  # Increase dist by 1 unit
result = vs.PointAlongPolyN(poly, dist, tolerance)
p2 = result[1]
vTan = result[2]
c = Marionette.TupleMap(lambda x, y: (y - x) ** 2, p1, p2)
l = math.sqrt(sum(c))

# outputs
self.Params.b.value = result[0]
self.Params.p2.value = result[1]
self.Params.vTan.value = result[2]

import vs
import math
import Marionette

@Marionette.NodeDefinition
class Params(metaclass=Marionette.OrderedClass):
# Erscheinungsbild
# Name
this = Marionette.Node("Get Point on Poly and Distance")
this.SetDescription('Gibt einen Punkt an der angegebenen Entfernung entlang des Polygons zurück, beginnend vom ersten Eckpunkt des Polygons, und berechnet die Entfernung zwischen zwei Punkten')

# Eingabeanschlüsse
poly = Marionette.PortIn(vs.Handle(0), 'hPoly')
poly.SetDescription("Das Eingabepolygon")
nDist = Marionette.PortIn(0, 'nDist')
nDist.SetDescription("Die angegebene Entfernung")
p1 = Marionette.PortIn((0, 0), 'p1')
p1.SetDescription("Ein 2D-Punkt, der den Startpunkt repräsentiert")
nSehne = Marionette.PortIn(0, 'nSehne')
nSehne.SetDescription("Eine angegebene Länge, die den Radius eines Kreises repräsentiert")

# OIP-Steuerungen
tolerance = Marionette.OIPControl('Toleranz', Marionette.WidgetType.Text, '0.0000000000001')
tolerance.SetDescription("Ein akzeptabler Unterschied zwischen den berechneten und approximierten Werten")

# Ausgabeanschlüsse
p2 = Marionette.PortOut('p2')
p2.SetDescription("Der Ergebnispunkt")
vTan = Marionette.PortOut('vTan')
vTan.SetDescription("Der Vektor tangential zum Polygon am Ergebnispunkt")
b = Marionette.PortOut('b')
b.SetDescription("True, wenn der Knoten seine Aufgabe erfolgreich abschließt, sonst False")

# VERHALTEN

def RunNode(self):
# Eingaben
poly = self.Params.poly.value
dist = self.Params.nDist.value
tolerance = self.Params.tolerance.value
p1 = self.Params.p1.value
nSehne = self.Params.nSehne.value

# Skript
result = vs.PointAlongPolyN(poly, dist, tolerance)
p2 = result[1]
vTan = result[2]

# Berechnen der Entfernung zwischen p1 und p2
c = Marionette.TupleMap(lambda x, y: (y - x) ** 2, p1, p2)
l = math.sqrt(sum(c))

# Annäherung in noch kleineren Schritten und Präzisierung in noch kleineren Schritten
while l < nSehne:
new_dist = dist + 0.000001  # Annäherung in noch kleineren Schritten
if new_dist >= nSehne:  # Überprüfe, ob die neue Entfernung nSehne überschreiten würde
break
else:
# Präzisierung in noch kleineren Schritten
step = 0.00000001
while dist < new_dist:
dist += step
result = vs.PointAlongPolyN(poly, dist, tolerance)
p2 = result[1]
vTan = result[2]
c = Marionette.TupleMap(lambda x, y: (y - x) ** 2, p1, p2)
l = math.sqrt(sum(c))

# Ausgaben
self.Params.b.value = result[0]
# Koordinaten mit höherer Genauigkeit erfassen
self.Params.p2.value = (round(result[1][0], 15), round(result[1][1], 15))
self.Params.vTan.value = result[2]

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

×   Pasted as rich text.   Restore formatting

Only 75 emoji are allowed.

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×

• KBASE
• #### MARIONETTE

×
• Create New...