12 Replies Latest reply on Nov 10, 2015 10:24 PM by Qwertyfly...

Convert 'almost smooth' and 'actually sharp' points to real ones

Sometimes in my work I see points-liars of two kinds:

1. Point which seems to be smooth — the angle between it's handles is very close to 180 degrees — but actually it is not. I guess there could be a (very slow) script, which goes through all points in selected objects and convert those of them which have angle between handles fit within defined value and turn into real smooth.

2. Point which says it is smooth, but looks sharp. You really see the angle, the corner, but when you try to tweak a handle of one of these, the opposite handle instantly remembers it's smooth and moves symmetrically, as a smooth point should. The problem is you can't always check every point's status in Control palette before tweaking! I'd like to have a script which I will conjure for every suspicious path and get rid of these little bastards.

• 1. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

food for thought:

this will find corners that are nearly smooth using my isSmooth function and a tolerance you can set.

will convert point type to smooth, but will not actually smooth the point.

so it actually creates a Liar of a point.

but could be enough to get started with.

I'm out of time and have to go.

can play more later...

```

// by Qwertyfly...

//this is not complete!

// angle tolerance for isSmooth function
var t = 1;

function isSmooth(Ax,Ay,Bx,By,Cx,Cy){
var angle = Math.abs(Math.atan2(Ay-By,Ax-Bx)-Math.atan2(Cy-By,Cx-Bx));
return (angle > Math.PI - t && angle < Math.PI + t);
}

var doc = app.activeDocument;
var sel = doc.selection;

for(var i=0; i<sel[0].pathPoints.length; i++){
var P = sel[0].pathPoints[i];

if(isSmooth(P.leftDirection[0],P.leftDirection[1],P.anchor[0],P.anchor[1],P.rightDirection[0],P.rightDirection[1])){
alert('smooth');
P.pointType = PointType.SMOOTH;
}
}
```
• 2. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

Testing.

I've created circle with four points at tangents and modified points like this:

1. left as it is

2. turned to corner point, but left handles unchanged

3. turned to corner point and changed the angle between them to 179

4. turned to corner point and changed the angle between them to 175

What I think the script should do:

1. nothing

2. turn point type to smooth

3. turn point type to smooth within tolerance set (1 degree) and change both angle between them to be exactly 180 degrees

4. nothing

What your code do now:

every point type turns smooth, but handles are left unchanged, which is the exact situation I want to get rid off

Liars, indeed.

I expected point 4 not be turned into it, but it strangely did.

• 3. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

in function the angle is a radian measurement.

6.28319 radians = approx 360 deg

so 3.14 or Pi = 180 deg

so 1 degree is approx 0.0174533 radians.

with a handle rotated 1 degree off smooth (179deg)

set t = 0.018 to get isSmooth to return false

set t = 0.017 to get isSmooth to return true

I did not mean to put a 1 in there.

it should have been much smaller...

making the 2 line up and be smooth is a little tricky.

which 1 do you move, which do you keep still, or do you move both a little?

this will affect the path and change the shape.

this math hurts me a little even if it is quite basic.

switched it up a bit, make it simpler to implement

it's missing the code to actually straighten handles to exactly 180 degrees.

try this:

```// by Qwertyfly...

//this is not complete!

// angle tolerance for isSmooth function
/*
tolerance is in radians
0.0174533 = apporx 1 degree
*/
var t = 0.018;

function isSmooth(P){
var angle = Math.abs(Math.atan2(P.leftDirection[1]-P.anchor[1],P.leftDirection[0]-P.anchor[0])
-Math.atan2(P.rightDirection[1]-P.anchor[1],P.rightDirection[0]-P.anchor[0]));
if(angle==Math.PI){return "smooth";}else{return(angle > Math.PI - t && angle < Math.PI + t);}
}

var doc = app.activeDocument;
var sel = doc.selection;
alert(sel[0].pathPoints.length);
for(var i=0; i<sel[0].pathPoints.length; i++){
var P = sel[0].pathPoints[i];

if(isSmooth(P)=="smooth"){
if(P.pointType == PointType.SMOOTH){
alert('super smooth');
}else{
alert('locking handles to smooth');
P.pointType = PointType.SMOOTH;
}
}else if(isSmooth(P)){ //true
alert('as good as smooth\nneed to straighten to 180 & lock handles to smooth');
P.pointType = PointType.SMOOTH;
//straighten code goes here
}else{ //false
alert('smooth as bitumen\nleaving as a corner');
}
}
```

• 4. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

Oh, radians.

Yes, tolerance is more convinient in degrees. I use PathScribe since BetterHandles and deal with anchors almost exceptionally within it's panel, which displays angle between handles.

Aligning both handles is crucial. The fact the point can be declared smooth without being actually smooth makes my mind stumble and edgy. Lies! I don't even understand, WHY it could be declared so when it's not true! There should be a tolerance, and all the process you are so kindly trying to conjure should work automatiacally under the hood. The sharp point with handles on one line is OK, of cource.

I think the most average way is to average angles, so move both a little. I also think the way when handle with more fractioned angle values moves to be lined up with more straight one is also could be very useful — like 89.7 will pair -90 (which won't change). The major angles, I guess, would be easier to set as a step between: 90 for example above, 5 for snapping to 15 rather then to 17, and so forth.

• 5. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

I developed that function as part of a larger script for removing points while keeping the path unchanged.

I was making headway with it but was never going to come close to the efficiency of PathScribe.

So I spent a few dollars and am very happy with the plugin.

I use it all the time!

• 6. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

Nevertheless, PathScribe is not almighty.

I had a long conversation with AstuteGraphics about SmartRemove function to fail at certain points near tangents. Trying to keep path unchanged, the algorithm seems to carry only about points, surrounding working segment, maintaining angles of adjacent handles, so when it slightly broken (pseudo-smooth point count too), all recalculated segment goes distorted. This happen very often in my work. After a week AstuteGraphics told me "to re-consider workflow", mentioning also they usually don't normally suggest "hold it in your other hand". Anyway, they do a great job pinning needles back to hedgehog.

Without PathScribe I won't be able to snap handles to grid in CC2015, because the "behaviour has been improved" by Adobe, as you might know, without checkbox to return it back.

• 7. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

after playing with my script, I understand the difficulty of modifying bezier curves. the math is more then my brain can handle.

so i don't expect it to be perfect.

i'll see if I can dig up some code to rotate about a point for straightening handles on a point that "should be smooth"

• 8. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

Wait a second.

What if script won't try to convert problematic points to smooth on it's own, but rather just select them? There is a button in PathScribe panel, that already can finish it with one click!

• 9. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

just feed each point wanted into an array and then cycle the array selecting the points.

this should not be hard at all.

• 10. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

something like this?

```// by Qwertyfly...

//this is not complete!

// angle tolerance for isSmooth function
/*
tolerance is in radians
0.0174533 = apporx 1 degree
*/
var t = 0.05;

function isSmooth(P){
var angle = Math.abs(Math.atan2(P.leftDirection[1]-P.anchor[1],P.leftDirection[0]-P.anchor[0])
-Math.atan2(P.rightDirection[1]-P.anchor[1],P.rightDirection[0]-P.anchor[0]));
if(angle==Math.PI){return "smooth";}else{return(angle > Math.PI - t && angle < Math.PI + t);}
}

var doc = app.activeDocument;
var sel = doc.selection;
var pointArray = {};
pointArray.a1 = [];
pointArray.a2 = [];
pointArray.a3 = [];
pointArray.a4 = [];
for(var i=0; i<sel[0].pathPoints.length; i++){
var P = sel[0].pathPoints[i];
if(isSmooth(P)=="smooth"){
if(P.pointType == PointType.SMOOTH){
//alert('super smooth');
pointArray.a1.push(i);
}else{
//alert('locking handles to smooth');
P.pointType = PointType.SMOOTH;
pointArray.a2.push(i);
}
}else if(isSmooth(P)){ //true
//alert('as good as smooth\nneed to straighten to 180 & lock handles to smooth');
P.pointType = PointType.SMOOTH;
//straighten code goes here
pointArray.a3.push(i);
}else{ //false
//alert('smooth as bitumen\nleaving as a corner');
pointArray.a4.push(i);
}
}
doc.selection = null;
var w = new Window('dialog','Pick your Points');
w.alignChildren = "left";
w.t0 = w.add('statictext',undefined,'Select the type of point you want to select');
w.g1 = w.add('group');
w.g1.orientation = "row";
w.g1.b1 = w.g1.add('button',undefined,'1');
w.g1.t1 = w.g1.add('statictext',undefined,'super smooth ' + pointArray.a1.length);
w.g1.b1.onClick = function () {w.close();selector(pointArray.a1);}
w.g2 = w.add('group');
w.g2.orientation = "row";
w.g2.b2 = w.g2.add('button',undefined,'2');
w.g2.t2 = w.g2.add('statictext',undefined,'now super smooth ' + pointArray.a2.length);
w.g2.b2.onClick = function () {w.close();selector(pointArray.a2);}
w.g3 = w.add('group');
w.g3.orientation = "row";
w.g3.b3 = w.g3.add('button',undefined,'3');
w.g3.t3 = w.g3.add('statictext',undefined,'should be smooth ' + pointArray.a3.length);
w.g3.b3.onClick = function () {w.close();selector(pointArray.a3);}
w.g4 = w.add('group');
w.g4.orientation = "row";
w.g4.b4 = w.g4.add('button',undefined,'4');
w.g4.t4 = w.g4.add('statictext',undefined,'corners ' + pointArray.a4.length);
w.g4.b4.onClick = function () {w.close();selector(pointArray.a4);}
w.show();
//alert(sel[0].pathPoints.length);
function selector(array){
for(var i=0; i<array.length; i++){
sel[0].pathPoints[array[i]].selected = PathPointSelection.ANCHORPOINT;
}
}
```
• 11. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

Damn! My approach I've managed to try before your post was too straight and less useful. You've done even more than I could have asked. The counters are very helpful.

Testing is done, it really selects right points! While tracking it, I thought that buttons should select but not close window (as Carlos did in his AlignText script, for exampe), so I'll try to rebuild is as palette.

Thanks a lot! It came out to be powerful tool.

• 12. Re: Convert 'almost smooth' and 'actually sharp' points to real ones

For something quick I don't like having to use Bridge Talk.

this would work well as an extension.

if I get any time in the next few days I might try it.