# Drawing FLines 3: Editing & Inspecting Lines

FLines contain a few methods for filtering their contents including `.visitPositions`, `.visitNodes`, `.visitPolar`. These methods are good for performing global transformations on the positions of elements. But there are many things that you can do with `FLine`s that are hard or impossible to formulate in the terms of these functions — what about editing individual nodes? inserting nodes? cutting up `FLine`s into pieces? or even figuring out intermediate points on lines that are not nodes?

The answer to these questions is to use a `FLine` `Cursor`. (A word about terminology: "cursor" to suggest your current position in a given line.) The easiest way of obtaining a cursor is directly from the `FLine` you'd like to inspect or edit:

``````cursor = someFLine.cursor()
``````

The rest of this page is really the reference documentation for cursors.

## Moving the Cursor

A cursor represents a point on a line — that point might be a node (one of the places that you `.moveTo` or `.lineTo`), or it might be a point, still on the line, between nodes.

Here's a random `FLine` that we'll use a cursor to access and modify:

``````myLine = FLine()
myLine.moveTo(75,75)
for n in range(0, 2):
myLine.polarCubicTo(1,3*Math.random()-1.5, 1, 1, 50+Math.random()*450,50+Math.random()*450)
if (Math.random()<0.8):
myLine.lineTo( * (Vector2(myLine.position).noise(250)))
``````

The first thing we can do with a `Cursor` is get its position:

``````def drawPoint(a):

myCursor = myLine.cursor();
drawPoint(myCursor.position())
``````

Giving:

The Cursor returned by `.cursor` defaults to starting at the beginning of the `FLine`.

A `Cursor` can be moved around a `FLine`; forwards & backwards from its current position, or skipping to a different position entirely.

``````for n in range(0, 30):
myCursor.forwardT(0.1)
drawPoint(myCursor.position())
``````

Draws:

`.forwardT(dt)` moves a cursor forward by an amount `dt` (backward for negative `dt`). There's also `.backwardT(dt)` which goes the other way. But what units is this 'T' in? Spline Parameter units. Note that each time around the for loop above we move forward by `0.1` — the first 10 points we draw are in the first curve segment, the first 10 are in the next segment (which is a straight line) and so on. 0.1 corresponds to 10% through the curve, regardless of the length of the curve. Thus the points in the second segment are much closer together than the first. In fact the points aren't typically evenly spaced within a curve segment (which you might be able to see in the third curve segment).

Mathematically this comes about because our Spline segments are specified as some function of a parameter (typically called 't' in the literature) which doesn't correspond to length along the curve. It doesn't necessarily correspond to anything at all — it's just a parameter that draws out the curve as it goes from 0 to 1. A drawn line can be thought of as the trace of an 'animation' of the pen that drew it — but all evidence concerning the speed of the pen has been lost. We just pick speeds that are fast and easy mathematically.

Sometimes this variable spacing is perfectly acceptable — if all you need is a point that's roughly in the middle between two nodes then `.forwardT(0.5)` will do fine.

Sometimes, it won't do fine. For those times — `.fowardD(distance)`:

``````myCursor = myLine.cursor()
drawPoint(myCursor.position())
for n in range(0, 80):
myCursor.forwardD(10)
drawPoint(myCursor.position())
``````

Will draw:

Now that you can move around the line via distance, we'll need to know how to convert 'T' units to 'D' units:

``````myCursor = myLine.cursor()
drawPoint(myCursor.position())

print myCursor.length(), myCursor.lengthT()
#a position that's 10 units along the spline
print myCursor.convertDToT(10)

#a position that's halfway between the 1st and 2nd node
print myCursor.convertTToD(1.5)

#which spline segment is the point 100 along the line in?
print int(myCursor.convertDToT(100))+1
``````

Put these ideas together and you can do something like this:

``````for n in range(0, 28):
myCursor.forwardD(20)
print "at 't' %s" % myCursor.currentT()
myCursor.forwardT(0.02)
print "at 'd' %s" % myCursor.currentD()
drawPoint(myCursor.position())
``````

Draws a more staccato rhythm:

For completeness, we should mention `.forwardNode()` (and `.backwardNode()`) as well as `.setT(t)` and `.setD(d)` which allow the `Cursor` random access to the `FLine`.

## Inspecting the contents of a `FLine`

Now that we have a point on a line, we can ask the Cursor for information about that point. `myCursor.tangentForward()` gives the forward tangent. This represents the direction that the line is heading in at that very point. Before a cubic split control point made by `.cubicTo(cx1, cy1, cx2, cy2, x2, y2)` this will always be equal to `(cx1, cy1)`. But at other points along the line it will be different. There's also `myCursor.tangentBackward()`. Usually this points the "other way" but in the same direction, though at control points that do not smoothly join sections together, the line connecting `.tangentBackward()`, `.position()` and `.tangentForward()` will not be straight.

Drawing these things at a number of points yields:

Or for a larger number of points:

Since these tangents are really a 'velocity' of how the line is 'moving' at a particular point, we can also build the 'acceleration' — `.accelerationForward()` and `.accelerationBackward()`. We can use the curvatures to draw osculating circles:

The code to draw this image serves as a nice example use of `FLine` and `Cursor`:

``````cursor = Cursor(myLine)
for n in range(0, 10):
p = cursor.position()
#drawPoint(p)
forwards = cursor.tangentForward()
backwards = cursor.tangentBackward()
if (forwards and backwards):
_self.lines.add(FLine().moveTo(*forwards).lineTo(*p).lineTo(*backwards)(color=Vector4(0.5, 0, 0, 0.5), derived=1, pointed=1))
elif (forwards):
_self.lines.add(FLine().moveTo(*forwards).lineTo(*p)(color=Vector4(0, 0, 0.5, 0.5), derived=1, pointed=1))
elif (backwards):
_self.lines.add(FLine().moveTo(*backwards).lineTo(*p)(color=Vector4(0.5, 0.5, 0, 0.5), derived=1, pointed=1))

try:
curve= cursor.accelerationForward()
if (curve):
_self.lines.add(FLine().moveTo(*(curve-p)*0.2+p).lineTo(*p)(color=Vector4(0, 0, 0, 0.5), derived=1, pointed=1))
except:
pass

#print 1/rr
if (forwards and rr!=0):
r = cursor.curvatureForward()
_self.lines.add(FLine().moveTo(*curve).lineTo(*p)(color=Vector4(0, 0, 1, 0.5), derived=1, pointed=1, color=Vector4(0,0,0,0.1)))

cursor.forwardT(0.25)
``````

### Editing FLines

There are two principle editing operations supported by Cursor as of this writing (we're still in the process of recasting our previous diverse and messy `FLine` manipulation code in terms of these `Cursor` objects).

• `.split()` — splits this FLine into two FLines at this Cursor. Returns a 2-tuple of FLine.
• `.insert()` — inserts a node at this position (and sets the `Cursor` to be at this node).

Visually, neither of these operations appear to do anything. The two lines that `.split()` creates seamlessly meet in the middle to look just like the old line; and the node that is inserted by `.insert()` has its parameters set such that the resulting line looks, again, just like the old one.

But having `.split()` you are free to move the resulting `FLine` apart, manipulating the individual elements independently:

A random noodle:

From this code example:

``````myLine = FLine()
myLine.moveTo(75,75)
for n in range(0, 4):
myLine.polarCubicTo(1,3*Math.random()-1.5, 1, 1, 50+Math.random()*450,50+Math.random()*450)

left = []
right = myLine.copy()
for n in range(0, 10):
c = Cursor(right)
c.forwardD(c.length()/divBy)
if (show): drawPoint(c.position())
a, b = c.split()
a, b = FLine(a), FLine(b)
left.append(a)
right = b

index = 0.0
for ll in left:
alpha = index/len(left)
ll+=Vector2(200+(1-alpha)*120,0)
ll+=rotation(index/12.0)
ll.visitPositions(lambda x,y : Vector2(x,y).noise(10))
ll(color=Color4(0,0,0.0, 0.3), pointed=1, pointColor=Color4(0,0,0.0, 0.5), derived=1, pointSize=1)
index+=1
return left

``````

## Making cursors by computation

All of the above techniques make a cursor by moving it around a `FLine` (and perhaps then editing that line in some way). Field has a number of other ways of making cursors, essentially techniques that might yield a point on a line yield a `PCursor` instead. This is always more useful than, say, a `Vector2` that just happens to be on a line.

### Minimum and minima

For example, what's the closest a line gets to a particular point?: `line.closet(point)` has one answer, it returns a cursor at the position on a line that's the closest this line gets to that position (a Vector2). Another, related, answer is `line.minima(point)` — this returns a list of cursors along `line` that are at locations where the line is at local minima with respect to that point. These are the places (including the closest point) where the line stops heading towards the point and starts heading away from the point.

An illustration:

Generated by code like this:

``````_self.lines.clear()

ll = FLine().moveTo(100,100).relCubicTo(1,-100, -100, 4, 200,300).relCubicTo(2, -400, 500, 300, 150, 150)
ll(pointed=1)

point = FLine().moveTo(400,400)(pointed=1)

#that curvy spline in black is hand edited, as is the point that we are looking for
_self.tweaks()

target = (FLine(_self.lines[1]).position)
mins = ll.minima(target)
minIs = ll.closest(target)

for mm in mins:
pp = FLine().moveTo(*mm.position())(pointed=1, color=Vector4(1,0,0,1))