Skip to content

Instantly share code, notes, and snippets.

@pgtwitter
Last active November 29, 2024 06:04
Show Gist options
  • Save pgtwitter/cbda64910da12bf447590dae0b279f2b to your computer and use it in GitHub Desktop.
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
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end
#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
@pgtwitter
Copy link
Author

スクリーンショット 2024-11-14 1 45 27

@pgtwitter
Copy link
Author

スクリーンショット 2024-11-29 15 04 02

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment