Last active
November 29, 2024 06:04
-
-
Save pgtwitter/cbda64910da12bf447590dae0b279f2b to your computer and use it in GitHub Desktop.
Find the distance between any point and a Bézier curve (find the parameter t that is the closest point on the Bézier curve). reference: https://shikitenkai.blogspot.com/2024/11/bezier.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Cocoa/Cocoa.h> | |
@interface AppDelegate : NSObject <NSApplicationDelegate> | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "AppDelegate.h" | |
@interface MyView : NSView | |
@end | |
@implementation MyView (bezier) | |
NSPoint vecSub(NSPoint p0,NSPoint p1){return NSMakePoint(p0.x-p1.x,p0.y-p1.y);} | |
float vecAbs(NSPoint p0){return sqrtf(p0.x*p0.x+p0.y*p0.y);} | |
NSPoint bezierPoint(NSPoint p0,NSPoint c0,NSPoint c1,NSPoint p1,float t) | |
{ | |
float coeff0= (1-t)*(1-t)*(1-t); | |
float coeff1= 3*(1-t)*(1-t)*t; | |
float coeff2= 3*(1-t)*t*t; | |
float coeff3= t*t*t; | |
float x= coeff0*p0.x+coeff1*c0.x+coeff2*c1.x+coeff3*p1.x; | |
float y= coeff0*p0.y+coeff1*c0.y+coeff2*c1.y+coeff3*p1.y; | |
return NSMakePoint(x,y); | |
} | |
float fn(float C0,float C1,float C2,float C3,float C4,float C5,float t) | |
{ | |
return C0+t*(C1+t*(C2+t*(C3+t*(C4+t*C5)))); | |
} | |
float fnd(float C1,float C2,float C3,float C4,float C5,float t) | |
{ | |
return C1+t*(2*C2+t*(3*C3+t*(4*C4+t*5*C5))); | |
} | |
float newton(float C0,float C1,float C2,float C3,float C4,float C5,float t0) | |
{ | |
float t= t0; | |
float by= 1E6; | |
float y= fn(C0,C1,C2,C3,C4,C5,t); | |
float diff= 1; | |
NSUInteger i=0; | |
for(i= 0;i<1E2&&fabs(y-by)>1E-2*fabs(y);i++) { | |
diff= fnd(C1,C2,C3,C4,C5,t); | |
if (fabs(diff)<1E-6) break; | |
t= t-y/diff; | |
by= y; | |
y= fn(C0,C1,C2,C3,C4,C5,t); | |
} | |
return t; | |
} | |
NSPoint distBezierAndPoint(NSPoint p0,NSPoint c0,NSPoint c1,NSPoint p1,NSPoint p) | |
{ | |
//reference https://shikitenkai.blogspot.com/2024/11/bezier.html | |
//reference https://shikitenkai.blogspot.com/2024/11/bezierqzp0t.html | |
NSPoint ap= vecSub(p0,p); | |
NSPoint bq= vecSub(c0,p); | |
NSPoint cr= vecSub(c1,p); | |
NSPoint ds= vecSub(p1,p); | |
float A=ap.x; | |
float B=-ap.x+bq.x; | |
float C=ap.x-2*bq.x+cr.x; | |
float D=-ap.x+3*bq.x-3*cr.x+ds.x; | |
float P=ap.y; | |
float Q=-ap.y+bq.y; | |
float R=ap.y-2*bq.y+cr.y; | |
float S=-ap.y+3*bq.y-3*cr.y+ds.y; | |
float C0= A*B+P*Q; | |
float C1= 2*A*C+3*B*B+2*P*R+3*Q*Q; | |
float C2= A*D+9*B*C+P*S+9*Q*R; | |
float C3= 4*B*D+6*C*C+4*Q*S+6*R*R; | |
float C4= 5*C*D+5*R*S; | |
float C5= D*D+S*S; | |
float distance0= vecAbs(vecSub(bezierPoint(p0,c0,c1,p1,0.0),p)); | |
float distance1= vecAbs(vecSub(bezierPoint(p0,c0,c1,p1,1.0),p)); | |
float minD= (distance0<distance1)?distance0:distance1; | |
float minDt= (distance0<distance1)?0.0:1.0; | |
for(NSUInteger i=0;i<10+1;i++) { | |
float t= newton(C0,C1,C2,C3,C4,C5,0.1*i); | |
t= (t<0.0)?0.0:(1.0<t?1.0:t); | |
float distance= vecAbs(vecSub(bezierPoint(p0,c0,c1,p1,t),p)); | |
if (minD>distance) {minD= distance;minDt= t;} | |
} | |
return NSMakePoint(minD,minDt); | |
} | |
@end | |
@implementation MyView | |
NSPoint randPoint(unsigned int w,unsigned int h) | |
{ | |
return NSMakePoint(arc4random_uniform(w),arc4random_uniform(h)); | |
} | |
void drawCurve(NSPoint p0,NSPoint c0,NSPoint c1,NSPoint p1) | |
{ | |
NSBezierPath *path= [NSBezierPath bezierPath]; | |
[path moveToPoint:p0]; | |
[path curveToPoint:p1 | |
controlPoint1:c0 | |
controlPoint2:c1]; | |
[path stroke]; | |
} | |
void drawLine(NSPoint p0,NSPoint p1) | |
{ | |
NSBezierPath *path= [NSBezierPath bezierPath]; | |
[path moveToPoint:p0]; | |
[path lineToPoint:p1]; | |
[path stroke]; | |
} | |
void drawAnchor(NSPoint p, float size) | |
{ | |
float hsize= size/2.0; | |
[[NSBezierPath bezierPathWithRect:NSMakeRect(p.x-hsize,p.y-hsize,size,size)] fill]; | |
} | |
void drawHandle(NSPoint p,NSPoint anchor,float size) | |
{ | |
drawLine(p,anchor); | |
float hsize= size/2.0; | |
[[NSBezierPath bezierPathWithRect:NSMakeRect(p.x-hsize,p.y-hsize,size,size)] fill]; | |
} | |
void cls(NSRect r) | |
{ | |
[[NSColor whiteColor] set]; | |
NSRectFill(r); | |
} | |
- (void)drawRect:(NSRect)dirtyRect | |
{ | |
cls(self.bounds); | |
NSSize bs= self.bounds.size; | |
NSPoint p0= NSMakePoint(bs.width/4,bs.height/2); | |
NSPoint c0= randPoint(bs.width,bs.height); | |
NSPoint c1= randPoint(bs.width,bs.height); | |
NSPoint p1= NSMakePoint(bs.width/4*3,bs.height/2); | |
for(NSUInteger i=0; i<1E3; i++) { | |
NSPoint p= randPoint(bs.width,bs.height); | |
NSPoint dt= distBezierAndPoint(p0,c0,c1,p1,p); | |
NSColor *c= (dt.y==1.0||dt.y==0)?[NSColor blueColor]:[NSColor greenColor]; | |
[c set]; | |
drawHandle(p, bezierPoint(p0,c0,c1,p1,dt.y), 3); | |
} | |
[[NSColor blackColor] set]; | |
drawCurve(p0,c0,c1,p1); | |
[[NSColor magentaColor] set]; | |
drawAnchor(p0,10); | |
drawAnchor(p1,10); | |
[[NSColor orangeColor] set]; | |
drawHandle(c0,p0,10); | |
drawHandle(c1,p1,10); | |
} | |
- (void)mouseDown:(NSEvent *)event | |
{ | |
[self setNeedsDisplay:YES]; | |
} | |
@end | |
@interface AppDelegate () | |
@property (strong) IBOutlet NSWindow *window; | |
@end | |
@implementation AppDelegate | |
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { | |
NSSize ws= self.window.contentView.frame.size; | |
MyView *myview= [[MyView alloc] initWithFrame:NSMakeRect(0,0,ws.width,ws.height)]; | |
[self.window.contentView addSubview:myview]; | |
} | |
- (void)applicationWillTerminate:(NSNotification *)aNotification { | |
} | |
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { | |
return YES; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment