-
The Continuing Odyssey of the Angle Bisector Problem
5 days ago • 0 commentsI asked Microsoft Co-pilot to write a C++ function that calculates the angle bisectors of an arbitrary triangle, without using branches or trig calls, and got this mess in response. Can you see what is wrong here? First of all, the response isn't in C++, but I am O.K., with that - I would just like an algorithm that works. Second, there is some conditional logic, which I stated was not desired, i.e. if the dot product is non-negative do this, or else do that. Lastly, the solution is of course completely wrong. Care to see a solution that at least "appears to work", at least for now? How about this one:
int triangle::generate_bisectors () { fpoint U1,U2,U3; MATH_TYPE d; MATH_TYPE dx1,dx2,dx3,dy1,dy2,dy3; MATH_TYPE L1,L2,L3; MATH_TYPE c1,c2,c3,s1,s2,s3; // first find the sines and cosines // of the line segments that make up // the triangle dx1 = current[1].x-current[0].x; dx2 = current[1].x-current[2].x; dx3 = current[2].x-current[0].x; dy1 = current[1].y-current[0].y; dy2 = current[1].y-current[2].y; dy3 = current[2].y-current[0].y; L1 = sqrt((dx1*dx1)+(dy1*dy1)); L2 = sqrt((dx2*dx2)+(dy2*dy2)); L3 = sqrt((dx3*dx3)+(dy3*dy3)); c1 = dx1*(1.0/L1); c2 = dx2*(1.0/L2); c3 = dx3*(1.0/L3); s1 = dy1*(1.0/L1); s2 = dy2*(1.0/L2); s3 = dy3*(1.0/L3); // now generate three points that // represent the directional unit // vectors for each line segment d = 1.0; // compass distance! U1.x = d*c1; U1.y = d*s1; U2.x = d*c2; U2.y = d*s2; U3.x = d*c3; U3.y = d*s3; // now generate the compass // points fpoint C1,C2,C3,C4,C5,C6; C1 = current[0]+U1; C4 = current[0]+U3; C2 = current[1]-U2; C5 = current[1]-U1; C3 = current[2]-U3; C6 = current[2]+U2; fpoint P0,P1,P2; // the directional unit vectors // can be used to find the midpint // of a line that besects an // isosceles triangle that forms // from the base of the "arrow" P0 = fpoint::midpoint (C1,C4); P1 = fpoint::midpoint (C2,C5); P2 = fpoint::midpoint (C3,C6); fline AP1,BP2,CP3; AP1 = fline (current[0],P0); BP2 = fline (current[1],P1); CP3 = fline (current[2],P2); J = fpoint::intersect (AP1,BC); K = fpoint::intersect (BP2,AC); L = fpoint::intersect (CP3,AB); return 0; }
Here is the method in other words. Using the Pythagorean identity, find the sines and cosines of each of the line segments. Then generate a set of unit vectors that correspond to each line segment. Add or subtract the unit vectors as appropriate to each vertex, to obtain a set of points equidistant from each vertex as if we were using a traditional compass. Find the midpoint of each "little arrow" so generated. Then generate a line segment that passes from each vertex through each compass midpoint, and then continue that line segment until it intersects the opposite side of the triangle. Optimization is hopefully a job that can be carried out by any good modern compiler, yet I unrolled some operations like calculating the sine and cosine values for the normalized unit vector for each line segment. Then I went ahead and used the implementations of some functions like midpoint and intersect in the point and fline classes which are defined elsewhere. Finding the intersection of two lines, when they are defined in parametric form is pretty simple, by coding an implementation of Cramer's rule, like this:
fpoint fpoint::intersect (const fline &L1, const fline &L2) { fpoint result; MATH_TYPE det,recip_det; det = L1.a*L2.b-L1.b*L2.a; if (det!=0) { recip_det = 1.0/det; result.x = (L1.b*L2.c-L1.c*L2.b)*recip_det; result.y = (L1.c*L2.a-L1.a*L2.c)*recip_det; result.m_bvalid = true; } else { result.m_bvalid = false; } return result; }
Of course, this means that I broke my own rule against conditional expressions., but on any CPU that has branch prediction, the test should always succeed, that is if we are being given valid triangles, so the performance cost should be minimal if not zero in terms of execution time. Or else, it could be #ifdef'd out in a more performance-based, and possibly in-lined version of the code. Haven't tried it yet on the pipelined CORDIC solver that is built into the Parallax Propeller P2 chip, or else what writing my own tessellation and convex hull routines would look like, let's say CUDA style, that is to say, to bypass OpenGL for other, non-graphics related things, like vector fields in electricity and magnetism, fluid mechanics, etc. Thus, the built-in methods are not always the best, the most desirable, or even useful, according to the situation. Enjoy!
-
Deep Reductionism vs. Intersectional Mosaicism?
5 days ago • 0 commentsConsider the operations of a sequence of simple affine transformations being carried out on an ordinary equilateral triangle. It should be obvious, in that case, that the angle bisectors and the midpoint bisectors of each angle and side respectively, all intersect at the same point, a point which is commonly referred to as the centroid. This property is invariant under simple shifting and rotation operations. However, if the triangle is stretched along any axis, or pair of axes, to transform it into another triangle, such as one that is isosceles, then even though this can be accomplished through ordinary linear operations, the nature of the angle bisectors is such that the apparent symmetry is broken, even though they remain convergent amongst themselves.
Now as far as the midpoint bisectors are concerned, they still result in the creation of a set of points that also can be used to subdivide any arbitrary triangle into four similar triangles, each one-fourth of the size of the original – allowing for recursive tessellation, of course. Yet if we introduce the angle bisectors into the process, and include them in our tessellation plan, this might provide a way to so also algorithmically generate a seemingly pseudo-random distribution of points, as it were, which might be useful for doing such things as defining the location of trees, blades of grass, hair follicles, or for defining other geometric forms.
The fact that Alan Turing himself was fascinated by the idea that oscillators regulated by chemical gradients might play a role in the determination of the distribution of stripes, or spots on such animals should not go un-noticed, either. Even if this is all regulated by DNA of course, a cell still has to ask as if it were, not just “What am I”, or “Who am I”, but “Where am I?
Here is an interesting question: What do you think about the idea of “intersectional mosaicism?” Not so much as a social construct, but rather, from the point of view of epistemology. Yet this is something that I should clarify since I am more likely to be influenced by such modern as Chomsky, Penrose, or even Escher. Yet another name comes to mind, and it is not Nietzsche. Sarte or Hagel, by the way. I will get to that later.
Let’s get back to the idea of “intersectional mosaicism” therefore, based upon an epistemological framework rooted in the approach to physics known as “deep reductionism”. Now some will theorize that everything is either male or female, or else is matter vs. energy, or it is math vs. geometry, or something like that. I am not saying that those things aren’t relevant, rather I should say that the simple cherry-picking of taxonomies just because one can use PowerPoint or draw Venn diagrams is not the answer either.
Yet, if we embrace “intersectional mosaicism” as a social construct, a question might arise whether there is some formal operational scheme, like Maslow’s hierarchy of needs, or Ericson’s stages of maturation that must somehow describe everything. Most of the time, such outlines are just as often as not, rooted in otherwise ad-hoc hypotheses at best, or else they are mostly pseudo-scientific constructions.
Whether to say, “to be or not to be”, or whether it is better to discuss the “is-ness of is”, or else “the being-ness of be” and what that means, let’s say, from the point of view of pure hedonism, is, another matter altogether. Or is it? In physics, spontaneous symmetry breaking, either is or is not, whatever Wikipedia says that is or is not, and that is all that I am going to say about that, for now.
Yet what if from the point of view of pedagogy, something else altogether has been overlooked? Something, that from the point of view of not only the mathematical framework of physics but so also from the point of view of the philosophical notion of the “construction of reality”, which might therefore have profound implications.
I started with some observations about triangles, which hopefully imply whatever that might imply. Maybe this is a good time to mention Minsky, and Winograd, among others, as promised earlier, and then move on to the next topic – a perennial favorite: wave matching in quantum systems, as well as applications, like turning audio into sheet music, and doing spectrum analysis using polyphase filter trees, when can then encapsulate multiple FFTs, each running with different bandwidths, sample rates, frequency ranges, sampling intervals, and so on. With everything being re-split, re-overlapped, and of course re-combined, as if by magic, as it were, so that one model will dominate, insofar as providing a higher temporal resolution concerning note onsets, than is normally possible, that is according to the Rayleigh limit as it is normally perceived. Then other methods of analysis provide enhanced frequency resolution, which is especially important at low frequencies.
An “intersectional mosaic” therefore, is not merely a “collage” as it were – as in some random art project, but something that has the potential to simultaneously blur traditional boundaries in regions of overlap, whether in a well-defined way or not. Yet this should hint, therefore, as to the notion that so-called principles of “deep reductionism” might apply, so also in some social constructs even – if I dare to say it – and I just did. For just as it should be obvious that the roots of logic are largely shared with geometry, and therefore algebraic topology.
Some will say that such ideas might invite some into the framework such things as theories about global economics, political upheavals, or those paths elsewhere that lead to the various wars, or opportunities for peace. But that gets way ahead of things. In the meantime, LLMs I think are deficient in their implementation details, insofar as the quality of the framework is concerned, that is to say – to the foundations of their world model. So maybe it is time to return to Euclid. Or at least it is for me at least, at least that is for a while.
-
A few "notes", just In case you never "noticed."
06/13/2024 at 00:53 • 0 commentsTurning audio into sheet music is one of my persistent passions. Experimenting with different polyphase filter tree topologies is always an adventure. 64? 128? 256? How many sub-bands? Sampling window size? That's another secret. Bessel, Butterworth or Gaussian window pre-processing, or a simple comb filter? That would be telling.
I've seen the comments in another forum that "if you are teaching magic, you are exposing magic!" And a LOT of people don't like that. But too bad! Maybe I should try decoding a certain famous Beatles chord. Not that it hasn't been done before. Yet in the meantime rtcp and llama integration is also a work in progress. Actually, this is a VERY old project - just doing some updates, therefore. Mainly what I am doing with this right now, then is fixing a lot of bugs, and doing a massive amount of project integration. Wouldn't it be interesting to get LLAMA working on the one hand, and to try training it - or at least a version of Mega Hal on some MIDI data derived from real music? O.K. that is one thing, but another is just simply getting over a dozen or so projects, with names like DDE, Frame-Lisp, Rubidium, Debug-Terminal, Euclid, Construction, and so on - to "talk to each other" so to speak - with a much higher level of project integration that spans multiple projects than anything ever tried before.
Of course - who remembers this code snippet, from the original UCSD Pascal Compiler?
IDENTIFIER = RECORD NAME: ALPHA; LLINK, RLINK: CTP; IDTYPE: STP; NEXT: CTP; CASE KLASS: IDCLASS OF KONST: (VALUES: VALU); FORMALVARS, ACTUALVARS: (VLEV: LEVRANGE; VADDR: ADDRRANGE; CASE BOOLEAN OF TRUE: (PUBLIC: BOOLEAN)); FIELD: (FLDADDR: ADDRRANGE; CASE FISPACKD: BOOLEAN OF TRUE: (FLDRBIT,FLDWIDTH: BITRANGE)); PROC, FUNC: (CASE PFDECKIND: DECLKIND OF SPECIAL: (KEY: INTEGER); STANDARD: (CSPNUM: INTEGER); DECLARED: (PFLEV: LEVRANGE; PFNAME: PROCRANGE; PFSEG: SEGRANGE; CASE PFKIND: IDKIND OF ACTUAL: (LOCALLC: ADDRRANGE; FORWDECL: BOOLEAN; EXTURNAL: BOOLEAN; INSCOPE: BOOLEAN; CASE BOOLEAN OF TRUE: (IMPORTED:BOOLEAN)))); MODULE: (SEGID: INTEGER) END;
That is, of course how the compiler keeps track of identifiers, while compiling another program of course, or even if it is compiling itself. Something is needed therefore, that goes beyond what people are doing with JSON, or other languages like RUBY or Python that allow creation of new data structures at run-time, which in principle is a form of "reflection". That doesn't mean that our computers are actually self-aware, of course, but remember what I said earlier about Narcissus, who turned to stone after staring at his own reflection, or else there was also "Echo" who pined away for the love of Narcissus, that is, until only her voice remained. All very cool.
So I was also checking out the download of the latest Ruby and Ruby on Rails distros, and it occurred to me that maybe I could add some Ruby integration as if maybe Ruby could just somehow be dropped into my framework - and then maybe, well things could get wild; because then I could pull in all of the Rails stuff, and maybe have "Ruby going off the rails - as it were."
And yet I still want to do something that works like SHRDLU, and then all of this needs to work with real physical robots.
-
Some shapes of things to come, among other things.
05/15/2024 at 20:50 • 0 commentsYeah, and in other news: I found my ancient IBM flowcharting template.
So while I have never been a big fan of flow charts, this might be a fun digression, given my latest obsession with DNA, hair, solar flares, and tessellations, with or without help from Euclid - of course. Weird to contemplate that there are different pre-defined symbols for magnetic vs. paper tape input, or else manual input vs. punch cards, and so on. A process seems to be a simple rectangle, whereas I/O operations are these other parallelograms. Yeah, yeah, yeah. And so it goes.
-
Whether it is Greek to You or Me ...
05/10/2024 at 05:18 • 0 commentsSo, I found a copy of Euclid's Elements online and started reviewing some of the classic geometric methods., and then I got an interesting idea. How hard would it be to implement all 200 or so proofs, or however many there are, in C++? Of course, when working with computer graphics we have to proceed analytically, but that doesn't mean that we can't, at least in principle come up with ways to do some of the traditional compass and straight-edge constructions, that everyone seems to have forgotten with all of the 3-D mania. Then again, there are some things quite wonderful and elegant about triangles, that are perhaps, underappreciated. Like the fact we can subdivide a triangle by bisecting the edges, or else we can subdivide a triangle by bisecting the angles, and the results are not necessarily the same! Much to the surprise of many, no doubt!
//////////////////////////////////////////////////////////////////// // // PROP. IX.—Problem. // To bisect a given rectilineal angle (BAC). // // THE ELEMENTS OF EUCLID - Translated by John Casey // Published 1887 by Cambridge Press, public domain. // //////////////////////////////////////////////////////////////////// MATH_TYPE fpoint::law_of_cosines (const fpoint &p1, const fpoint &p2, const fpoint &p3) { MATH_TYPE A,bc,a2,b2,c2; MATH_TYPE dx1,dy1,dx2,dy2,dx3,dy3; MATH_TYPE result; dx1 = p2.x-p3.x; dy1 = p2.y-p3.y; a2 = (dx1dx1)+(dy1dy1); dx2 = p1.x-p2.x; dy2 = p1.y-p3.y; b2 = (dx2dx2)+(dy2dy2); dx3 = p1.x-p3.x; dy3 = p1.y-p3.y; c2 = (dx3dx3)+(dy3dy3); bc = sqrt(b2c2); result = (b2+c2-a2)/(2bc); return result; }
Well, here it is, therefore, in all of its glory - as we saw earlier, at least one method for subdividing triangles must be lurking somewhere, and by invoking such a process more or less recursively, we might want to consider how this can be extended to perhaps offer at least one way of generating the point cloud for a full head of hair, or a field of grass, or whatever else might come to the imagination. Solar flares anyone?
What else might you be expecting? And then along came a spider, maybe? That also seems doable! There are so many different ways to build our model space, and not just with triangles or quads. It might also be as if a web or a mesh might represent something else altogether, just like the fabric of space-time itself. This thought is so deep and so perfect, that when we contemplate the meaning of it - the perfect entanglement that is, in some other realm.
WARNING: While the law of cosines method that I have described here seems to work most of the time, that is for most well-behaved triangles in the first quadrant, some triangles with obtuse angles. or other issues seem to give incorrect results, such as returning angle bisectors that are perpendicular to the correct angles. In the meantime, have fun with this if you dare, since what I want to do with it is figure out a way to generate the bisectors, as well as do a bunch of other constructions, where feasible that is, without requiring any branches in the code! And THAT is not at all as simple as it might seem when having to not only contemplate quadrant issues, obtuseness, rotations, etc. Nonetheless, for purposes of CUDA style or OpenMP style parallelism, and/or pipelining, the more useful work that can be done without needing to have conditional logic, obviously should produce a substantial benefit when the problem at hand is put to scale, regardless of architecture.