Created
October 31, 2023 22:02
-
-
Save osnr/ac144dffcb6b248cb240da7dc1648389 to your computer and use it in GitHub Desktop.
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
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 = ®ion_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