Skip to content

Instantly share code, notes, and snippets.

@osnr
Created October 31, 2023 22:02
Show Gist options
  • Save osnr/ac144dffcb6b248cb240da7dc1648389 to your computer and use it in GitHub Desktop.
Save osnr/ac144dffcb6b248cb240da7dc1648389 to your computer and use it in GitHub Desktop.
diff --git a/lib/math.tcl b/lib/math.tcl
index 5bff734..48b7f0c 100644
--- a/lib/math.tcl
+++ b/lib/math.tcl
@@ -1,9 +1,17 @@
namespace eval ::vec2 {
- proc add {a b} {
- list [+ [lindex $a 0] [lindex $b 0]] [+ [lindex $a 1] [lindex $b 1]]
+ set cc [c create]
+ $cc code {
+ typedef struct Vec2f { float x; float y; } Vec2f;
}
- proc sub {a b} {
- list [- [lindex $a 0] [lindex $b 0]] [- [lindex $a 1] [lindex $b 1]]
+
+ $cc proc add {Vec2f a Vec2f b} Vec2f {
+ return (Vec2f) { .x = a.x + b.x, .y = a.y + b.y };
+ }
+ $cc proc sub {Vec2f a Vec2f b} Vec2f {
+ return (Vec2f) { .x = a.x - b.x, .y = a.y - b.y };
+ }
+ $cc proc scale {Vec2f a float sx float sy} Vec2f {
+ return (Vec2f) { .x = a.x * sx, .y = a.y * sy };
}
proc scale {a args} {
if {[llength $args] == 1} {
@@ -11,24 +19,30 @@ namespace eval ::vec2 {
} else {
lassign $args sx sy
}
- list [* [lindex $a 0] $sx] [* [lindex $a 1] $sy]
+ scale $a $sx $sy
}
- proc rotate {a theta} {
- lassign $a x y
- list [expr {$x*cos($theta) + $y*sin($theta)}] \
- [expr {-$x*sin($theta) + $y*cos($theta)}]
+ $cc proc rotate {Vec2f a float theta} Vec2f {
+ return (Vec2f) {
+ .x = a.x*cosf(theta) + a.y*sinf(theta),
+ .y = -a.x*sinf(theta) + a.y*cosf(theta)
+ };
}
- proc distance {a b} {
- lassign $a ax ay
- lassign $b bx by
- expr {sqrt(pow($ax-$bx, 2) + pow($ay-$by, 2))}
+ $cc proc distance {Vec2f a Vec2f b} float {
+ return sqrtf(powf(a.x - b.x, 2), powf(a.y - b.y, 2));
}
- proc normalize {a} {
- set l2 [vec2 distance $a [list 0 0]]
- vec2 scale [/ 1 $l2] $a
+ $cc proc normalize {Vec2f a} Vec2f {
+ float l2 = distance(a, (Vec2f) { .x = 0, .y = 0 });
+ return scale(a, 1.0f/l2, 1.0f/l2);
}
- proc dot {a b} {
- expr {[lindex $a 0]*[lindex $b 0] + [lindex $a 1]*[lindex $b 1]}
+ $cc proc dot {Vec2f a Vec2f b} float {
+ return a.x*b.x + a.y*b.y;
+ }
+ $cc proc distanceToLineSegment {Vec2f a Vec2f v Vec2f w} float {
+ float l2 = distance(v, w);
+ if (l2 == 0.0f) {
+ return distance(a, v);
+ }
+ float t =
}
proc distanceToLineSegment {a v w} {
set l2 [vec2 distance $v $w]
@@ -44,6 +58,80 @@ namespace eval ::vec2 {
}
namespace eval ::region {
+ set cc [c create]
+ $cc code {
+ typedef struct edge_t {
+ int from;
+ int to;
+ } edge_t;
+
+ typedef struct region_t {
+ int32_t nvertices;
+ Vec2f* vertices;
+
+ int32_t nedges;
+ edge_t* edges;
+
+ float angle;
+ } region_t;
+
+ void region_t_freeIntRepProc(Tcl_Obj *objPtr) {
+ region_t *r = (region_t *)objPtr->internalRep.otherValuePtr;
+ ckfree(r->vertices);
+ ckfree(r->edges);
+ ckfree(r);
+ }
+ static region_t dup(region_t r);
+ void region_t_dupIntRepProc(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) {
+ dupPtr->internalRep.otherValuePtr = dup(srcPtr->internalRep.otherValuePtr);
+ }
+ void region_t_updateStringProc(Tcl_Obj *objPtr) {
+ region_t *r = (region_t *)objPtr->internalRep.otherValuePtr;
+ Tcl_DString s;
+ Tcl_DStringInit(&s);
+
+ Tcl_DStringStartSublist(&s);
+ for (int i = 0; i < r->nvertices; i++) {
+ char buf[100]; snprintf(buf, 100, "{%f %f}", r->vertices[i].x, r->vertices[i].y);
+ Tcl_DStringAppendElement(&s, buf);
+ }
+ Tcl_DStringEndSublist(&s);
+
+ Tcl_DStringStartSublist(&s);
+ for (int i = 0; i < r->nedges; i++) {
+ char buf[100]; snprintf(buf, 100, "{%d %d}", r->edges[i].from, r->edges[i].to);
+ Tcl_DStringAppendElement(&s, buf);
+ }
+ Tcl_DStringEndSublist(&s);
+
+ char anglebuf[100]; snprintf(anglebuf, 100, "%f", r->angle);
+ Tcl_DStringAppendElement(&s, anglebuf);
+
+ objPtr->bytes = s.string;
+ }
+ int region_t_setFromAnyProc(Tcl_Interp *interp, Tcl_Obj *objPtr) {
+
+ }
+ Tcl_ObjType region_t_ObjType = (Tcl_ObjType) {
+ .freeIntRepProc = region_t_freeIntRepProc,
+ .dupIntRepProc = region_t_dupIntRepProc,
+ .updateStringProc = region_t_updateStringProc,
+ .setFromAnyProc = region_t_setFromAnyProc
+ };
+ }
+ $cc argtype region_t {
+ region_t $argname;
+ // Allocate an array for vertices
+ // Allocate an array for edges
+ }
+ $cc rtype region_t {
+ $robj = Tcl_NewObj();
+ $robj->bytes = NULL;
+ $robj->typePtr = &region_t_ObjType;
+ $robj->internalRep.otherValuePtr = ckalloc(sizeof(region_t));
+ memcpy($robj->internalRep.otherValuePtr, &rv, sizeof(region_t));
+ }
+
proc vertices {r} { lindex $r 0 }
proc edges {r} { lindex $r 1 }
# Angle the region is rotated above the horizontal, in radians:
@@ -51,25 +139,42 @@ namespace eval ::region {
expr {[llength $r] >= 3 ? [lindex $r 2] : 0}
}
- proc width {r} {
- set minXp 100000
- set maxXp -100000
- foreach v [vertices [rotate $r [* -1 [angle $r]]]] {
- lassign $v xp yp
- if {$xp < $minXp} { set minXp $xp }
- if {$xp > $maxXp} { set maxXp $xp }
+ $cc proc dup {region_t r} region_t {
+ region_t ret = r;
+ ret.vertices = ckalloc(ret.nvertices * sizeof(Vec2f));
+ memcpy(ret.vertices, r.vertices, ret.nvertices * sizeof(Vec2f));
+ ret.edges = ckalloc(ret.nedges * sizeof(edge_t));
+ memcpy(ret.edges, r.edges, ret.nedges * sizeof(edge_t));
+ return ret;
+ }
+ $cc proc dispose {region_t r} void {
+ ckfree(r.vertices);
+ ckfree(r.edges);
+ }
+
+ $cc proc width {region_t r} float {
+ float minXp = 100000;
+ float maxXp = -100000;
+ region_t unrotatedR = rotate(r, -angle(r));
+ for (int i = 0; i < unrotatedR.nvertices; i++) {
+ Vec2f v = unrotatedR.vertices[i];
+ if (v.x < minXp) { minXp = v.x; }
+ if (v.x > maxXp) { maxXp = v.x; }
}
- expr { $maxXp - $minXp }
- }
- proc height {r} {
- set minYp 100000
- set maxYp -100000
- foreach v [vertices [rotate $r [* -1 [angle $r]]]] {
- lassign $v xp yp
- if {$yp < $minYp} { set minYp $yp }
- if {$yp > $maxYp} { set maxYp $yp }
+ dispose(r);
+ return maxXp - minXp;
+ }
+ $cc proc height {region_t r} float {
+ float minYp = 100000;
+ float maxYp = -100000;
+ region_t unrotatedR = rotate(r, -angle(r));
+ for (int i = 0; i < unrotatedR.nvertices; i++) {
+ Vec2f v = unrotatedR.vertices[i];
+ if (v.y < minYp) { minYp = v.y; }
+ if (v.y > maxYp) { maxYp = v.y; }
}
- expr { $maxYp - $minYp }
+ dispose(r);
+ return maxYp - minYp;
}
proc mapVertices {varname r body} {
@@ -123,17 +228,33 @@ namespace eval ::region {
vec2 scale $vecsum 0.25
}
- proc rotate {r angle} {
- set theta [angle $r]
- set c [centroid $r]
- set r' [mapVertices v $r {
- set v [vec2 sub $v $c]
- set v [vec2 rotate $v $angle]
- set v [vec2 add $v $c]
- set v
- }]
- lset r' 2 [+ $theta $angle]
- set r'
+ $cc proc rotate {region_t r float angle} region_t {
+ Vec2f c = centroid(r);
+ region_t ret = dup(r);
+ for (int i = 0; i < ret.nvertices; i++) {
+ Vec2f v = ret.vertices[i];
+ v = Vec2f_sub(v, c);
+ v = Vec2f_rotate(v, angle);
+ v = Vec2f_add(v, c);
+ ret.vertices[i] = v;
+ }
+ ret.angle += angle;
+ return ret;
+ }
+
+ $cc proc scaleImpl {region_t r float sx float sy} region_t {
+ float theta = r.angle;
+ region_t ret = dup(r);
+ for (int i = 0; i < ret.nvertices; i++) {
+ Vec2f v = ret.vertices[i];
+ v = Vec2f_sub(v, c);
+ v = Vec2f_rotate(v, -theta);
+ v = Vec2f_scale(v, sx, sy);
+ v = Vec2f_rotate(v, theta);
+ v = Vec2f_add(v, c);
+ ret.vertices[i] = v;
+ }
+ return ret;
}
# Scales about the center of the region, along the x and y axes of
@@ -166,15 +287,7 @@ namespace eval ::region {
error "region scale: Invalid dimension $dim"
}
- # TODO: Optimize
- set r [mapVertices v $r {
- set v [vec2 sub $v $c]
- set v [vec2 rotate $v [* -1 $theta]]
- set v [vec2 scale $v $sxp $syp]
- set v [vec2 rotate $v $theta]
- set v [vec2 add $v $c]
- set v
- }]
+ set r [scaleImpl $r $sxp $syp]
}
set r
}
@@ -215,6 +328,8 @@ namespace eval ::region {
namespace ensemble create
}
+$mathcc compile
+
proc rectanglesOverlap {P1 P2 Q1 Q2 strict} {
set b1x1 [lindex $P1 0]
set b1y1 [lindex $P1 1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment