GeoGad - The Geometry Gadget
Contents
Introduction
GeoGad is short for "Geometry Gadget" and it is a programming language I wrote and implemented as part of a geometric modeler. When anyone ever creates a new programming language, there is one dominant question which must be answered: For the love of god, why!?
The purpose of this language is primarily to support user interactive input. Having become completely convinced of the utility and brilliance of Hewlett-Packard’s Reverse Polish Notation calculators for user facing calculation work, I thought that such a system would be ideal for technical geometry modeling. Another influence that proves the utility of this approach with geometry is PostScript. Unfortunately, PostScript is not ideal to work with interactively and it is mired in two dimensions. GeoGad seeks to create an RPN/RPL style language like that found on later HP calculators such as the beloved HP-48 and HP-28, for example, which will be ideal for working with accurate technical geometry.
The language is currently implemented in Python and pretty much anything Python can do can be quite easily implemented in this language as a function. It can be thought of as a way to turn Python into an interactive HP Reverse Polish Lisp kind of experience. I have been using it as a very powerful calculator pretty much every day now since 2005.
Download
The source code is available for you to take a look at and play with. This is mostly to easily have gg available to me. It is also for my friends and people who would like to see what kind of software I write and like. The URL is simply this.
Unpack this tar file and run the gg
executable with Python 3.
Starting from a safe directory (such as /tmp
) you can download and
run GeoGad with this single line. (Or do it in two steps and review it
to your satisfaction first.)
:->[/tmp]$ wget -qO- xed.ch/p/gg/gg.tgz | tar xz ; gg/gg
=== The Geometry Gadget ===
GeoGad Interpreter - Version alpha-14.5
Chris X Edwards - www.xed.ch
Copyright 2005-2014 - GNU Public License
gg--> 5 range [10] 5 :* * ''Seems to work!''
/--------------------
| (2) LST:[VAL:0.0, VAL:10.0, VAL:20.0, VAL:30.0, VAL:40.0]
| (1) TXT:Seems to work!
\--------------------
gg--> exit
Thanks! Bye!
Try entering the (space-separated) items in my test line one at a time to get a feel for what it is doing.
Geometry
In 2014, I finally was able to write a substantial portion of the
functionality to actually work with geometry. The way this
works is that there is a module called mm
which stands for "memory
model" and is responsible for keeping track of the geometry while it
is in memory and available to the user through the GeoGad interface.
This memory model is mainly made of "entities". All entities and
geometry are fully three dimensional. Currently there are only two
kinds of entities, "lines" and "tri faces". It’s important to note
that the lines are visible while the tri faces are not. The only
purpose to the tri faces is to provide opaque surfaces used in hidden
line removal. If you want a visible tri face, create a tri face and
then three lines that use its vertices.
The points that define lines and tri faces come from a list of
points that is automatically managed by the mm
module. The user never
needs to access the points of the memory model directly. The list of
points is always automatically normalized to contain the fewest
number of points that will suffice to define all entities.
The model shown above was generated entirely with GeoGad.
Any time anything is done to the memory model’s geometry which would
create a noticeable change, the entire model is dumped to the to2d
program. The to2d
program is a unix-style command-line rendering
engine that I wrote in Cpp. It takes as its input a list of numbered
points followed by records of either two or three fields which are
point numbers comprising lines or tri faces respectively. Options to
the to2d
program control various aspects of the rendering such as
camera placement and orientation. GeoGad has functions to control
these options from the interpreter. The to2d
program is very
powerful and can make a full 3d vector model into a completely
accurate 2d vector model of the rendered scene. This includes hidden
line removal, shallow angle culls, and other features.
The output of to2d
is a series of lines in a format compatible with
scalable vector graphics (SVG) syntax. This is collected and prepared
by GeoGad into an HTML file which is saved in /tmp/gg.html
by
default. By pointing a web browser to this file, the model can be
seen. The HTML file has a refresh feature which will cause it to
reload itself so that as the user works with the memory model, the
results are continuously displayed in the browser.
This vector image (check the page’s source if you’re interested)
was generated with my to2d
Unix command line rendering engine. It’s
not perfect (I count two errors), but it’s pretty decent with hidden
line removals considering that you get 2d geometry, not just a raster
image.
Language
The GeoGad language is conceptually very simple. Input objects are sent to an evaluator which produces output objects. These output objects are sent to a FILO stack where they can be accessed by the evaluator in subsequent operations.
RPN - Reverse Lisp
The most powerful programming language is Lisp. If you don’t know Lisp, you don’t know what it means for a programming language to be powerful and elegant. Once you learn Lisp, you will understand what is lacking in most other languages.
If you are familiar with RPN, feel free to skip this section. If you’re not used to PostScript or RPN calculators, here’s a quick demonstration of how simple things typically work in such systems including GeoGad.
In a normal calculator or programming language, if you want to add "3"
to "4" you would use syntax like 3+4=
or x=3+4
. This is very
different from GeoGad which is stack based. To accomplish this task,
put the 3
on the stack ("3" then "Enter") then put the 4
on the
stack, and finally put a +
on the stack. The last part, the +
is
immediately removed and "evaluated". It is a symbol that maps to an
addition function. This function starts by pulling two values off the
stack, in this case the 3
and 4
, and then adding them together.
The result of this is then put back on the stack.
For those not already absolutely delighted by this way of doing business, this kind of "reverse" thinking can be very disturbing. Why would anyone want to do this? For one thing, it turns out that this is a very efficient way for computers to manage affairs. However, there are benefits for humans as well. Most of the syntax for traditional languages comes from trying to approximate the look of math notation handed down through the ages. Is this traditional math notation ideal? Nope. Definitely not. To see why let’s look at an example. Imagine typing this into a pocket calculator:
3 + 4 * 2 =
Although programming languages can generally get this right, the calculator would fail (or require some fancy programming). The problem is that the order of operations is not well understood by a cheap calculator. In normal programming, if you wanted to do the addition first, you’d need:
( 3 + 4 ) * 2 =
Compare both of these with their equivalent syntax in GeoGad:
4 2 * 3 +
3 4 + 2 *
Or how about this example from the source code of GeoGad which burned me. This is part of the calculation for finding a point a certain ratio between two points. Considering just one axis, here is what I started with:
x= P1[0]-P0[0] * ratio + P0[0]
That produced comically wrong answers until I hunted down that I was missing parentheses.
x= (P1[0]-P0[0]) * ratio + P0[0]
It looks easy and obvious when it’s pointed out but clearly this kind of mistake can happen all too easily. In this case the spacing I used reflected wishful thinking rather than correct code. The RPN style languages simply do not let you think in ways that can produce errors like this.
There are no sneaky operator precedence rules. Parentheses are never
needed. Things are put on the stack and evaluated as they arrive and
there is never ambiguity. Notice also that the GeoGad way does not
require an =
sign. The +
operator immediately takes two values off
the stack and pushes the sum back on the stack. There’s nothing more
to be done.
Once you get used to the idea, it’s quite useful to be able to use the stack as an infinite scratch place to store and manipulate intermediate results. Having a stack is also useful for calculations that may have many outputs (e.g. value of sine between 0 and 30 every 10 degrees) or many inputs (e.g. find the average of these numbers).
4 range |[sin::!] map wake ! -> 0.0 0.017452 0.034899 0.052335
0.0 0.017452 0.034899 0.052335 4 2list avg -> 0.0261715
If you’re still not quite getting the idea, this video very patiently explains RPN.
Transaction Notation
First a quick note about the notation used in GeoGad documentation. If objects A, B, and C are sent to the evaluator in that order, and D is output first, E second, and F third to the stack, then the notation is:
A B C -> D E F
This is a shorthand of the entire interaction which could have transaction notation explicitly for each operation. For example:
1 2 3 -> 1 2 3
This could also be laboriously written:
1 -> 1
2 -> 1 2
3 -> 1 2 3
Also, pre-existing objects on the stack which are not affected can be assumed in the transaction notation:
static_stuff_not_shown A B C -> static_stuff_not_shown D E F
Language Format
GeoGad is not line-based. End of line codes are just like spaces.
Spaces are the default interobject delimiter. Any series of objects
separated by spaces will be sent to the evaluator in that order. Some
special characters always should be separated from other objects and
therefore, ironically, you don’t have to include them yourself. The
interpreter will add them automatically for you. It is helpful,
however to understand that they are needed, but the interpreter is
just adding them for you. The special characters that get autospaced
are [
, ]
, !
, |
, #
, and ::
. So if you want to name a symbol
something very odd like odd#s
, it will not work. Instead, the
interpreter will send three symbols to the evaluator, odd
, #
, and
s
, as if you had typed odd # s
.
Object Types
To achieve a degree of user simplicity, there are only four types of objects in the entire language.
Short | Label | Type | Example |
---|---|---|---|
T |
TXT |
Literal |
|
V |
VAL |
Value |
|
S |
SYM |
Symbol |
|
L |
LST |
List |
|
Objects can be dead or live. Dead objects are inert and have no special functionality while live objects can undergo some spontaneous change. TXT and VAL objects are always dead. The evaluator can only copy, delete, or synthesize these objects.
''Text'' -> TXT:Text
1066 -> VAL:1066.0
SYM objects are always live. The practical effect of this is that symbols spontaneously resolve themselves into what they stand for whenever possible. LST objects can be either dead or alive though their default is to be dead. A live list which is evaluated will evaluate each item the list contains in order. Therefore a live list is like a function or macro.
Think of it like this - Literal and value objects are like oak or pine wood: you want the tree to be dead and cut into lumber for it to be useful. Symbol objects are like lemons or avocados: you want the live tree to produce something else (fruit). List objects are like walnut or cherry: sometimes you want the dead tree and sometimes you want what it produces when alive.
Text Literals
It will be helpful to understand the motivation behind the odd style of text input. In engineering work (in the USA), it is very common in geometrical work to have text like this:
(3) 1' 2-1/4" C'BORE 2" DP.
This presents a bunch of common and irritating problems. With GeoGad, this text string is entered like this:
''(3) 1' 2-1/4" C'BORE 2" DP.'' -> TXT:(3) 1' 2-1/4" C'BORE 2" DP.
The main problem with this kind of thing is how then do you specify two single quotes?
This is where the magic character comes in. Normally it is ^.
Here’s an example of this which does typically occur annoyingly when describing the system itself. The text that is typed in at the prompt in this example is crafted to produce text objects whose text is accurate (they themselves are not accurate because of the escaping necessary).
gg--> ''Text is specified with two apostrophe (') characters.''
gg--> ''Typing '^'foo'^' would make a text object "TXT:foo".''
gg--> ''Note that to get that text '^^' was typed where a '^' was desired.''
gg--> ''This can be repeated indefinitely to get '^^^' and so on.''
/--------------------
| (4) TXT:Text is specified with two apostrophe (') characters.
| (3) TXT:Typing ''foo'' would make a text object "TXT:foo".
| (2) TXT:Note that to get that text '^' was typed where a '' was desired.
| (1) TXT:This can be repeated indefinitely to get '^^' and so on.
\--------------------
Generally, this won’t be used very much in practical situations. Basically, any pair of quotes surrounding nothing but magic characters returns the original with one of the magic characters missing.
gg--> '''^^^^^'''
/--------------------
| (1) TXT:'^^^^'
\--------------------
This allows for entering pretty much any arbitrarily odd and messy
text so long as it doesn’t use the very rare idea of two apostrophes.
Double apostrophes are arguably no more difficult to type than
shift-apostrophe (or a double quote). I felt that this was a better
system than using single or double quotes (or backticks) with annoying
escapes every time you want to say "Don\'t use \"quotes\" ironically."
Note that the current interpreter isn’t very good about doing the right thing when it comes to complicated text problems in conjunction with special auto-spaced characters. Use caution in such cases.
Symbols
Symbols tend to undergo a one-way change into the object they represent. This is the natural behavior:
X1 -> TXT:Value of X1
Symbols can be protected from the evaluator by preparing the stack to contain a protector which is the pipe, "|" which itself is a symbol and a special auto-spaced character.
|X1 -> SYM:X1
This allows symbols to be used as symbols and not their contents. This is essential for defining the contents (among other things). The symbol sto dereferences to a built-in command that assigns the value on level 2 of the stack to the symbol on level 1.
3 |X1 sto X1 -> VAL:3.0
| 3 |X1 sto X1 -> SYM:X1
To force an evaluation, use the evaluator symbol, !.
| 3 |X1 sto X1! -> VAL:3.0
|! -> SYM:!
|!! -> ** Empty Stack **
Symbols continue to recursively dereference in a single operation until it is no longer possible.
10 |X sto |X |Y sto Y -> VAL:10.0
Values
If an object is not explicitly marked as text or a list, then it is a symbol or a value. If it can be made into a value, it is. The situation this might cause problems with is with exponential notation. For example:
|1e1 -> SYM:| VAL:10.0
|1ee1 -> SYM:1ee1
There is no distinction between integers and reals. Save that for fancy inconvenient-to-humans languages. In gg, a value is a numeric value with no strings attached.
Lists
Lists are collections of objects. They can be recursive i.e. they can contain lists. Lists are reducible but durable, i.e. they tend not to be reduced unless explicitly made to do so.
[''Text'' 3 |X1 [3 2]] ->
LST:[TXT:Text, VAL:3.0, SYM:|, SYM:X1, LST:[VAL:3.0, VAL:2.0]]
The basic way to collect some actions into what is normally a function is to put it in a special list. Lists can hold anything (other lists, symbols, symbols which are commands, values, text). As a default, lists have the property of being inert. An inert list just contains stuff and that is that. A list can however be awoken with the wake command. Here is an example of a normal list doing not much of anything but holding data:
[3 5 +] -> LST:[VAL:3.0, VAL:5.0, SYM:+]
But if you "wake" this list, an attribute is set that allows its contents to be deployed and evaluated on the stack as commands. When a live list is evaluated, the objects it contains are sent to the evaluator as fresh input in order.
[3 5 +] wake -> LST:[VAL:3.0, VAL:5.0, SYM:+]<!>
Note the <!>
attached to the list. Lists like this can be activated
so that each item it contains will be put on the stack in order and
evaluated. To evaluate it use the eval
command.
[3 5 +] wake eval -> VAL:8.0
This can be shortened with the special syntax in the name space
property of the list. Since the list is not just data if it is to be
executed, it needs no naming. Instead of names, using !
will signify
that the list is to be an executable list.
[3 5 +::!] -> VAL:8.0
Notice that putting a "waked" list on the stack immediately evaluates
it. To protect it, defer evaluation with a |
.
|[3 5 +::!] -> LST:[VAL:3.0, VAL:5.0, SYM:+]<!>
The eval
command has an alias of !
which allows for this kind of
syntax to be somewhat shorter.
|[3 5 +::!] ! -> VAL:8.0
The ability to wake lists into functional code allows for the creation
of macros. When a list that has the wake property activated is stored
into a symbol with the sto
command, it creates a named block of
executable code. When the symbol name is put on the stack, the list is
evaluated.
Here is a simple (but useful) macro that clears the stack.
|clear |c sto -> ** Empty Stack **
''some'' ''stack'' ''cruft'' c -> ** Empty Stack **
Here’s an example which converts miles into kilometers.
|[12 5280 * 25.4 * 1e6 / *::!] |tokm sto -> ** Empty Stack **
26.2 tokm -> VAL:42.1648128
If you want to free up any name associated with a stored variable, you
can use the unsto
.
|tokm unsto -> ** Empty Stack **
Named List Items
Sometimes it is useful to be able to refer to a member object of a list by some name. This is like using a hash or dictionary in other languages, but here it is less efficient or clever. The advantage is that the list is still ordered. Also, these namespace symbols are optional. The symbols are put into a name area like this:
[39 25 16::x y z]
There is a one to one ordered correspondence between names and list objects. If there are too many names, the extras represent null values. If there are insufficient names, the extra objects are unnamed.
[39 25 16::x y z w]
[39 25 16::x]
List names are used like this:
[39 25 16::x y z] |P1 sto P1.y -> VAL:25.0
This will be confusing for the interpreter, but it won’t cause a crash and will just return nothing to the stack.
[39 25 16::x y z w] |P1 sto P1.w ->
If the name used isn’t in the list’s namespace, then the whole list comes back.
[39 25 16::x] |P1 sto P1.y -> LST:[VAL:3.0, VAL:9.0]<x>
The list namespace can also be used to wake lists. This is true because the typical use for lists is either as a container of named data or a function macro, usually not both. However the list object was designed so that it could be easily switch back and forth between the two modes. To create a live list use this syntax:
12 [ 25.4 * ::!] -> VAL:304.8
That’s actually a bit useless. This is more useful:
|[ 25.4 * ::!] |2mm sto 12 2mm -> VAL:304.8
Once a list is already created either on the stack or stored in a
variable in memory, it is possible to access and edit the list’s name
set. The names
command takes a list or SYM of a list from v1 and if
that list has a name component, they are extracted into a separate
list as TXT objects.
The decorate
function takes a list from v2 and a name list from v1
and replaces the v2 list’s name space with the value of v1. The name
list must be composed of only TXT or SYM objects. If they are SYM
objects, they are not resolved but used as names (the SYMs need not be
defined). This should also take a SYM of a list. If that is the case,
the output should be nothing but the SYM should contain the updated
list. If a LST is supplied as v2, then an updated list is returned.
Any existing names are lost.
[1 2 3] [one two three] decorate -> [1 2 3]<one two three>
[7 8 9] |X sto |X [i j k] decorate X.j -> 8
To change the naming of items in a list, names
can be used with
repl
and decorate
.
[100 .9784 .8591 .5798::jpy usd franc pound] |money sto
|money money names 3 [''chf'' ''gbp''] repl decorate
money -> [100 .9784 .8591 .5798::jpy usd chf gbp]
Name Scope And Local Variables
When a non-dead list is having its components evaluated, it is sometimes desirable to have access to variables that are local only to the evaluation of the list. The namespace can be used to achieve that effect too. Any names found in a namespace which also contains a ! will be treated as local variables which are active and accessible only for the duration of the execution of the list. Note the position of the ! or any of the names does not matter in this context.
|[|X sto |Y sto Y Y * X X * + sqrt::! X Y] |R sto ->
3 4 R -> VAL:5.0
In order to create functions that take values off the stack and then work with them, there is a local variable loader function →. Using it allows the function in the previous example to be written:
|[-> Y Y * X X * + sqrt::!X Y] |R sto ->
3 4 R -> VAL:5.0
Any local variable name that has the same name as something in the global symbol table, will mask the global version.
''Original X'' |X sto 3 4 R X -> VAL:5 TXT:Original X
Lists which are themselves components of a list can access the namespace of the parent list if it is not overridden with a local definition specific to the sublist.
1 |A sto
[2 |A sto ''2b'' |b sto
[3 |A sto
[4 |A sto A b::!A]
A::!A]
A::!A]
A -> VAL:4.0 TXT:2b VAL:3.0 VAL:2.0 VAL:1.0
OOP
List namespaces can be used to create a facsimile of object-oriented behavior where data and methods can be encapsulated.
gg--> [3 [dup Repeater.times -- repeat::!]::Times Exec] |Repeater sto
** Empty Stack **
gg--> ''Hip hip hooray!'' Repeater.Exec
/--------------------
| (3) TXT:Hip hip hooray!
| (2) TXT:Hip hip hooray!
| (1) TXT:Hip hip hooray!
\--------------------
gg--> clear 2 |Repeater.Times sto
** Empty Stack **
gg--> ''Two cheers!'' Repeater.Exec
/--------------------
| (2) TXT:Two cheers!
| (1) TXT:Two cheers!
\--------------------
Note
|
See the Bugs section to avoid getting burned by the current known problems with this. |
List Functions
-
2list
- Takes a number from v1 and v1’s value more stack objects and produces a list of size v1.->list
hp is an alias.`... OBn ... OB1 n 2list ==> ... [OBn ... OB1]`
-
len
- Returns the number of items in the LST from v1 or the number of characters if v1 is a TXT object.size
hp is an alias. -
headn
- Returns a list or substring made of the first v1 items of a list or string (respectively) at v2. -
head
hp - Same as1 headn
. -
tailn
- Returns a list or substring made of the last v1 items of a list or string (respectively) at v2. -
tail
hp - Same as1 tailn
-
list->
hp Same aswake !
-
sort
hp - Takes a LST object from v1 and returns a LST object where the components of v1 are sorted in ascending order. The items should all be TXT or VAL and of the same kind. -
revlist
hp - Takes a LST object from v1 and returns a LST object where the components of v1 are reversed in order. This allows for a descending sort withsort revlist
. Works on TXT objecst too. Aliasrev
. This is actually very similar to-1 :*
.4 range rev -> [3 2 1 0] ''poorcryptography'' rev -> yhpargotpyrcroop
-
get
hp - Returns from v2 LST object the value of the list component whose position is specified by v1.[1.1 2.2 3.3 4.4] 3 get -> 3.3
-
getn
- From a LST or SYM of a LST on v2, this function returns the value of the list component whose position is specified by v1 where v1 is a name from a named list. This can be specified as a SYM or TXT value. The alias is>>
.[1.1 2.2 3.3 4.4::x y z w] |z getn -> 3.3 [1.1 2.2 3.3 4.4::x y z w] ''y'' >> -> 2.2 11 22 33 pointform |myp sto |myp |y >> -> 22
-
put
hp - Takes list from v3 and a position from v2 and replaces the v3 item at v2 with the value of v1. This should also take a SYM of a list. If that is the case, the output should be nothing but the SYM should contain the updated list. If a LST is supplied as v3, then an updated list is returned. -
putn
- Put by name. Takes list from v3 and a position from v2 and replaces the named component of v3 at position specified with v2 with the value of v1. This command also takes a SYM of a list for v3. If that is the case, the output should be nothing but the SYM should contain the updated list. If a LST is supplied as v3, then an updated list is returned. The alias is<<
.1 2 3 pointform |y 99 << -> [1 99 3]<x y z>
-
sub
hp - Takes a string or list object from v3 and a start position from v2 and an end position from v1 and returns the sub-string or sub-list (respectively) from v2 to v1. If v1 is less than v2, an empty string or list is returned. Any positional value less than 1 is treated as 1. Any positional value greater than thelen
of v3 will be treated as thelen
of v3. The positional values are inclusive such that if v2 is 2 and v3 is 4, both the 2nd (and 3rd) and 4th position items will be part of the returned sub-object.''abcdefghijklmnopqrstuvwxyz'' 19 21 sub -> ''stu''
-
repl
hp - Takes a string or list object from v3 and a position from v2 and a replacement string or list object from v1 and replaces the portion of v3 starting at v2 with v1. If v2 is less than 1 or greater than thelen
of v3, the items are simply concatenated, v1+v3 and v3+v1 respectively. -
pos
hp - Takes a LST or TXT item from v2. If v2 is a LST then take an item from v1 and return the position v1 is found in v2. If v2 is a TXT itme, then v1 must be a TXT item and the value returned is the position where the substring v2 is found in v1. -
sum
- Produces the total of all values in a list. There is an aliastot
hp.8 range sum -> 28
Comments
There is no special comment syntax per se, but there is a helpful
alias provided to make commenting programs easier. The # is a
special auto-spaced character that is mapped to the drop command. So
to include a comment, simply put a text object on the stack and
immediately follow it with a #
. That’s basically an expensive null
operation.
''This is a comment in the GeoGad idiom!''#
Using #
interactively for drop is pretty handy too.
Evaluator Functions
-
help
- Print a list of available functions. Not really too helpful at this point. -
version
- Some nice information about the program. This is executed on startup. -
exit
- Quit the interpreter. Ctrl-D or end of file also works. -
quit
- Alias ofexit
. -
sto
hp - Stores object X in the symbol table reference by symbol S. Note that PostScript’sdef
function has the input the other way around. PostScript is wrong.X S sto ->
-
eval
- Takes X off the stack and uses that object as input. The results depend on what happens.X eval -> output
-
!
- Alias ofeval
. This is a special auto-spaced character.X! -> output
-
wake
- List L becomes live so that subsequent evaluation will cause each object it contains to be evaluated in order.L wake -> L<!>
-
inert
- Kill a list so that subsequent evaluations just produce the list itself.L<x> inert -> L
-
len
- Counts how many objects are in a list or text literal.L len -> V T len -> V
Stack Functions
-
dropn
hp - Drops n items from the list.Xn ... X1 n ->
-
drop
hp - Alias of1 dropn
. -
#
- Alias of1 dropn
. Also a special auto-spaced character. Used immediately after text literals, this command produces a method for specifying comments in programs. -
clear
hp - Clear the entire stack.X... clear -> ** Empty Stack **
Here’s a macro to map clear to "c" which can be handy. Note that CTRL-L will clear the screen, but not the stack.
-
depth
hp - Count number of items on the stack.X... depth -> ... V
-
yank
- Remove an item on level n of the stack and put it on level 1.Xn+1 Xn Xn-1 ... X1 n yank -> Xn+1 Xn-1 ...X1 Xn
-
placen
- Take object on level 2 and place it at the level specified on level 1.Xn+1 Xn Xn-1 ... X1 n placen -> Xn+1 X1 Xn-1 ...
-
dupn
hp - Get all the objects on levels n through 1 of the stack and put them on the stack again.Xn ... X1 n dupn -> Xn ... X1 Xn ... X1
-
pickn
- Copy an object at level n and add the copy to the stack. There is an aliaspick
hp which does the exact same thing.Xn ... X1 n pickn -> Xn ... X1 Xn
-
swap
hp - alias of2 yank
. PostScript cryptically calls thisexch
. -
drop2
hp - Alias of2 dropn
. -
drop3
- Alias of3 dropn
. -
dup
hp - Alias of1 dupn
. -
dup2
hp - Alias of2 dupn
. -
dup3
- Alias of3 dupn
. -
rot
hp - Alias of3 yank
. -
over
hp - Alias of2 pickn
.
Mathematical Functions
-
e_
- Constant with value of 2.7182818284590451. -
pi
- Constant with value of 3.1415926535897931. -
mul
- Produces the product of the value objects on levels 1 and 2. Item multiplication operator where the values are the focus. The common useful alias is*
. Compare to:*
where lists are the focus. SYMs are all resolved to their referent objects. Valid inputs from v1 and v2:-
VAL VAL * → simple multiplication
-
TXT VAL * → replication of text into concatenated text, commutative input
-
LST_of_VALs VAL * → list of each list item multiplied by VAL, commutative input
-
LST_of_VALs LST_of_VALs * → pad smaller with 1 and multiply each ith pos
If two lists are multiplied, the shorter one is padded with ones to match the other. In the case of two lists and both have list names, the longest list’s are used. Examples:
2 2 * -> 4 2 [1 2 3] * -> [2 4 6] 31.831 |diameter sto |diameter |pi * -> 100.000035756 [pi pi pi] pi inv * -> [1 1 1] [1 2 3] 2 * -> [2 4 6] [1 2 3] [1 2] * -> [1 4 3] Pad the shorter list with ones. [2 2] [4 4 4::x y z] * -> [8 8 4]<x y z> [1 2] [1 2 3] * -> [1 4 3] 4 ''ha'' * -> ''hahahaha'' ''backwards'' -2 * -> ''sdrawkcabsdrawkcab''
-
-
neg
- Alias of-1 mul
.[3 pi -5] neg -> [-3.0 -3.14159265359 5.0] ''rats mood loot'' neg -> tool doom star
-
lmul
- List multiplication operator where lists are the focus. The common useful alias is:*
. Compare to*
where items are the focus. All SYMs are resolved to their referent. The input requires only that v1 or v2 or both must resolve to a VAL. All other combinations are valid. A negative VAL as the multiplier will reverse the order of multiplied lists.-
L V :* → replication of list items in list, commutative
-
T V :* → put text item in list with copies, commutative
-
V V :* → put v2 in list with v1 copies, not commutative
Examples:
[1 2 3] 2 :* -> [1 2 3 1 2 3] 2 [1 2 3] :* -> [1 2 3 1 2 3] ''abc'' 2 :* -> [''abc'' ''abc''] 4 3 :* -> [4 4 4] 3 4 :* -> [3 3 3 3] [0 1] -3 :* -> [1 0 1 0 1 0]
-
-
add
- Item addition operator where the values are the focus. The common useful alias is+
. Compare to:+
where lists are the focus. SYMs are all resolved to their referent objects. Valid inputs from v1 and v2:-
TXT TXT + → concatenate text
-
VAL VAL + → simple addition, commutative
-
LST_of_VALs VAL + → add VAL to all items in list (of values), commutative
-
LST_of_VALs LST_of_VALs + → add each corresponding position, commutative
If two lists are added, the shorter one is padded with zeros to match the other. If two lists both have list names, the first list’s are used. Examples:
2 2 + -> 4 pi pi + 2deg -> 360 [pi 10::a b] |Q sto -180 2rad Q.a + -> 0 [4 8] 3 + -> [7 11] [4 8] [2 3] + -> [6 11] [4 8] [2 3] + -> [6 11] [4 8 3] [2 3] + -> [6 11 3] [4 8] [1 2 3] + -> [5 10 3] [0 4] [1 2 3] + -> [1 6 3] [1 2 3::a b c] [2 2 2::x y z] + -> [3 4 5]<a b c> ''xed'' ''.ch'' + -> ''xed.ch''
-
-
ladd
- List addition operator where lists are the focus. Compare to+
where items are the focus. Given the existence of v1 and v2, all input possibilities are valid. Only SYMs that ultimately refer to LSTs are resolved prior to the actual operation. Name space lists are lost.-
L ? :+ → append item to end of list
-
? L :+ → prepend item to front of list
-
L L :+ → concatenate lists in order [v2 v1]
-
notL notL :+ → make list containing [notL notL]
Examples:
[4 8] 7 :+ -> [4 8 7] 4 [4 8] :+ -> [4 4 8] [1 2 3] |Q sto |Q 4 :+ -> [1 2 3 4] [2 ''two''] [3 3 3] :+ -> [2 ''two'' 3 3 3] [3 3 3] [2 2] :+ -> [3 3 3 2 2] ''listify'' ''me'' :+ [''listify'' ''me'']
-
-
sub
- Alias ofneg add
. -
-
- Alias ofsub
. -
++
- Alias of1 add
. -
--
- Alias of1 sub
. -
div
- Produces the quotient of the value objects on levels 1 and 2.V V div -> V
-
/
- Alias ofdiv
. -
inv
- Alias of1 swap div
. -
idiv
- Produces the integer division of the value objects on levels 1 and 2.v2 v1 idiv -> v2/v1
-
mod
- Produces the modulus of the value objects on levels 1 and 2.Va Vb mod -> v1_mod_vb
-
abs
- Absolute value.V abs -> V
-
floor
- The lowest whole number nearest to V.V floor -> V
-
ceil
- The highest whole number nearest to V.V ceil -> V
-
pow
- The value on level 2 raised to the power of the value on level 1.V V pow -> V
-
xroot
hp - Computes v1 th root of v2. Alias ofinv pow
27 3 xroot -> 3
-
sqrt
- The square root of the value at v1.V sqrt -> V
-
sq
hp - Square the value at v1. Alias ofdup *
. -
exp
- The constant e raised to the power of Vx.Vx exp -> V
-
logbase
- The Vb base log of Vx.Vx Vb logbase -> V
-
log
- Natural log.V log -> V
-
log10
- Base 10 log.V log10 -> V
-
radmode
- Changes angle mode to radians. Trig functions assume radian inputs and inverse trig functions produce radian angles. This will create the variable_gg_angmode
to maintain this state; it can be set manually too withsto
andunsto
. -
degmode
- Changes angle mode to degrees. Trig functions assume degree inputs and inverse trig functions produce degree angles. This is the default and the assumed mode when the state variable is not set. -
2deg
- Converts a value from radians to degrees. If the value is a list containing no other type but VAL for the first three positions (or fewer), then the list is interpreted as containing degrees, arcminutes (if present), and arcseconds (if also present). Empty lists return zero and more than three values in a list are ignored.Vr 2deg -> Vd [45.0 30.0 0.0::degrees arcminutes arcseconds] 2deg -> 45.5
-
2rad
- Converts a value from degrees to radians. This also works like2deg
with lists of degree, arcminutes, and arcseconds. This does not need the angle mode set to radians since it is already explicit.Vd 2rad -> Vr [180 0 0] 2rad -> 3.141592653589793
-
2dms
- Convert the angle supplied to a list representing degrees, arcminutes, and arcseconds. This function defaults to treating the supplied angle as degrees but ifradmode
has been called, it will assume radians.degmode 45.5 2dms -> [45.0 30.0 0.0::degrees arcminutes arcseconds] 45.5 2rad radmode 2dms -> [45.0 30.0 0.0::degrees arcminutes arcseconds]
-
sin
- Sine of the angle argument specified. Input units are assumed to be degrees unlessradmode
has been called.V sin -> V 90 sin -> 1.0 degmode 30 sin radmode pi 6 / sin -> 0.5 0.5
-
cos
- Cosine of the angle argument specified. Input units are assumed to be degrees unlessradmode
has been called.V cos -> V 0 cos -> 1.0
-
tan
- Tangent of the angle argument specified. Input units are assumed to be degrees unlessradmode
has been called.V tan -> V 45 tan -> 1.0
-
asin
- Arcsine. Also respectsradmode
anddegmode
for angle modes. Input needs to be between -1 and 1.V asin -> V
-
acos
- Arccosine. Also respectsradmode
anddegmode
for angle modes. Input needs to be between -1 and 1.V acos -> V
-
atan
- Arctangent. Also respectsradmode
anddegmode
for angle modes.1 atan -> 45 5 inv atan 4 * 239 inv atan - 4 * 2rad -> 3.14159265359
Statistics Functions
-
rand
hp - Returns a random value between 0 and 1. -
rdz
hp - Takes v1 off the stack and uses it to set the random seed. If the stack is empty or v1 is zero, use current time. Set this with known objects to get repeatable random sequences. -
shuffle
- Takes a list item from v1 and returns a list of the same objects in a completely random order. The following will shuffle the entire stack.depth 2list shuffle wake !
-
fact
hp - Factorial of the value at v1. It is helpful to remember that the factorial is the number of permutations of n distinct objects. For example, the number of anagrams of a word is the factorial of the word length (if all letters are different - if not, look into "multisets"). This function name was actually HP28 syntax which was preserved on the HP48. It’s useful here since the normal syntax for factorial is busy doing other important things. This is not (yet) a real Gamma function. Factorials get big in a hurry so avoid large numbers; 170 is the max allowable for now.18 fact -> 6.40237370573e+15
-
comb
hp - Number of possible combinations of v2 items taken v1 at a time. A poker hand is an example. Imagine 52 (v2) distinct cards drawn 5 (v1) at a time. Order does not matter and each is only used once. This creates the possibilities for around 2.6 million possible poker hands.fact(v2)/(fact(v1)*fact(v2-v1)) 52 5 comb -> 2598960
-
perm
hp - Number of possible permutations of v2 items taken v1 at a time. In real life, permutation often answers the question, how many ways can N items be ordered? For example, if you want to know how many ways there are arrange a family of 4 at a 4 person table, use 4 for v1 and v2. If there are 8 dogs racing and you want to know how many different ways the top 3 could be filled, use 8 for v2 and 3 for v1. If a keysafe has 10 buttons, 4 of which must be set in the correct order to open, how many possible ways can this be tried?fact(v2) / fact(v2-v1) 4 4 perm -> 24 8 3 perm -> 336 10 4 perm -> 5040
-
distgauss
- Takes VALs from v1 and v2 and returns a value randomly generated by a function that produces values which are distributed in a Gaussian distribution. The v2 VAL is the mean of the distribution. The v1 VAL is the distribution’s standard deviation. There is an aliasndist
hp.|[.5 .2 distgauss::!] 100000 repeat 100000 2list dup mean swap sdev -> 0.500337044014 0.200301690936
-
distexp
- Takes VAL from v1 and returns a value randomly generated by a function that produces values which are distributed in an exponential distribution. The v1 VAL is Lambda which is a parameter of this kind of distribution. The formula isf(x,Lambda)= Lambda * exp(-Lambda * x)
The mean is 1/Lambda and the variance is 1/(Lambda*Lambda).|[.75 distexp::!] 100000 repeat 100000 2list dup mean inv swap var inv sqrt -> 0.743701781531 0.742319134654
-
distweibull
- Takes VALs from v1 and v2, and returns a value randomly generated by a function that produces values which are distributed in a Weibull distribution. This is often used to model processes related to failure rates, survival analysis, and reliability. The v2 VAL is Lambda which is the scale parameter for this kind of distribution. The v1 VAL is k which is the shape parameter. Both must be greater than zero. If k is 1 this is equivalent to the exponential distribution. If k is 2 it is equivalent to the Rayleigh distribution. A value of k < 1 indicates that the failure rate decreases over time. This happens if there is significant "infant mortality", or defective items failing early and the failure rate decreasing over time as the defective items are weeded out of the population. A value of k = 1 indicates that the failure rate is constant over time. This might suggest random external events are causing mortality, or failure. A value of k > 1 indicates that the failure rate increases with time. This happens if there is an "aging" process, or parts that are more likely to fail as time goes on.|[.75 1 distweibull::!] 100000 repeat 100000 2list mean -> 0.751247734665
-
max
hp - Returns the maximum value of a list from v1. If the list contains symbols, they should get resolved at least one level deep. Simple lists should work as expected. Extremely complex lists may be an adventure.[2 e_ 3 pi 1] -> pi |rand 100 repeat 100 2list max -> 0.998288151032
-
min
hp - Finds the minimum value in a list at v1. Similar behavior tomax
.[4 e_ 3 7] min -> e_ [4 e_ 3 2 7] min -> 2
-
avg
- Produces the average, or to be precise, the arithmetic mean of all value items contained in a list at v1. There is an aliasmean
hp.8 range avg -> 3.5
-
weightedmean
- Theweightedmean
function takes a list of numbers to be averaged from v2 and a list of weights to bias the result by. The lists must be or resolve to VALs and they must be the same length. This is commonly used to calculate weighted grades. If a student gets grades of C, A, A, D, B (corresponding to [2 4 4 1 3]) in classes of 4, 3, 3, 4, 3 credits respectively, the weighted average of these two lists will give a GPA adjusted for the "importance" based on credits. This could also be used to make product recommendations based on how well rated an item is by a list of people (list v2) and how closely each of those people match the user’s previous tastes (list v1, the weights).[2 4 4 1 3] [4 3 3 4 3] weightedmean -> 2.64705882353 [2 4 4 1 3] mean -> 2.8 [76 89 83 79 91 95 82 69 66 75 80 88 ] [2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] weightedmean -> 82.3834355828 [76 89 83 79 91 95 82 69 66 75 80 88 ] mean -> 81.0833333333
-
var
hp - Calculate the sample variance of a list of values. Use this when some of the members of the entire population are not represented.[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] var -> 0.285151515152 [76 89 83 79 91 95 82 69 66 75 80 88] var -> 77.1742424242
-
pvar
hp - Calculate the population variance of a list of values. Use this when all of the members of the entire population are represented.[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] var -> 0.261388888889 [76 89 83 79 91 95 82 69 66 75 80 88] var -> 70.7430555556
-
sdev
hp - Calculate the sample standard deviation of a list of values. Use this when some of the members of the entire population are not represented. This is the square root of the sample variance.[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] sdev -> 0.533995800687 [76 89 83 79 91 95 82 69 66 75 80 88] sdev -> 8.78488716059
-
psdev
hp - Calculate the population standard deviation of a list of values. Use this when all of the members of the entire population are represented. This is the square root of the population variance[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] var -> 0.511262055006 [76 89 83 79 91 95 82 69 66 75 80 88] var -> 8.41088910613
-
cov
hp - Calculate the sample covariance of two lists of values taken from v1 and v2. The two lists should be the same length. Use this when some of the members of the entire population are not represented.[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] [76 89 83 79 91 95 82 69 66 75 80 88 ] cov -> 3.85303030303
-
pcov
hp - Calculate the population covariance of two lists of values taken from v1 and v2. The two lists should be the same length. Use this when all of the members of the entire population are represented.[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] [76 89 83 79 91 95 82 69 66 75 80 88 ] pcov -> 3.53194444444
-
corr
hp - Calculate the sample Pearson’s correleation coefficient (r) between two lists of values taken from v1 and v2. The two lists should be the same length. The Pearson product-moment correlation coefficient (PMCC) is a numerical value between -1 and 1 that measures the strength of the linear relationship or association between two variables.When r is closer to 1 it indicates a strong positive relationship. A value of 0 indicates that there is no relationship. Values close to -1 signal a strong negative relationship between the two variables. The basic composition of the Pearson PMCC is the covariance of the two lists divided by the product of each list’s standard deviation. Note: It is possible to specify the correlation coefficient in terms of population statistics. That was done and the actual answers produced were the same indicating that the differences (the1/(1-n)
vs1/n
) cancel in this process. This is why there is not "population correlation coefficient" even though such a thing has a name, rho. The HP48 also scrupulously had population and sample statistics for everything exceptcorr
. And those folks knew what they were doing![2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] [76 89 83 79 91 95 82 69 66 75 80 88 ] corr -> 0.821350253246
-
tanimoto
- Thetanimoto
function takes a list of properties of item A from v2 and a list of properties of item B from v1 and calculates the Tanimoto similarity coefficient. The idea is to compute a measure of how similar these things A and B are based on their characteristics. For example, if you’re an alien naturalist and you find a mystery animal with "horns", "tail", "beard", "red", and "hoof", and you want to see if its more like a zebra ("tail", "stripes", "hoof", "mane") or a goat ("horns", "tail", "hoof", "beard") this might be helpful. In recommendation engines, this can be used to find people with similar properties. There is much confusion aboout the origin and use of this. It seems to be a generalization of the Jaccard Index applicable to sets, which is what this function assumes. The formula used here isT= Ni/(Na+Nb-Ni)
where Ni is the number of items in both lists or the count of the intersection, Na is the number of items in list A, and Nb is the number of items in list B. Dissimilarity, a distance metric, can be acheived with1 rot rot tanimoto -
. The properties can be specified as TXT or VAL or SYM. If they are SYM, they are resolved.[''horns'' ''tail'' ''beard'' ''red'' ''hoof''] |devil sto devil [''tail'' ''stripes'' ''hoof'' ''mane''] tanimoto -> 0.285714285714 devil [''horns'' ''beard'' ''tail'' ''hoof''] tanimoto -> .8
-
entropy
- Calculate the Shannon entropy for a list of items. This is a measure of how surprising a random selection of the data is. It can be used to measure the amount of disorder in a set. Lowering entropy during an iterative classification process could be a sign that the data is becoming better organized. Symbols do get resolved although other data stays as is. The formula isH(X)=-sum[i=1,n](p(x)*log2(p(x)))
where p(x) is the probability that X is in state x. The n is the number of unique items in the original list. The log2 aspect causes this function to return output measured in bits of entropy. This entropy calculation is likely not correct for cryptographic keys and passphrases since the probabilities of a certain value are generally lower than this process assumes (by counting extant examples).[2.2 2.4 3.1 2.5 3.5 3.6 2.5 2.0 2.2 2.6 2.7 3.3] entropy -> 3.25162916739 [76 89 83 79 91 95 82 69 66 75 80 88] entropy -> 3.58496250072 [''p'' ''a'' ''s'' ''s'' ''w'' ''o'' ''r'' ''d''] entropy -> 2.75 [''3'' ''T'' ''^'' '','' ''d'' ''9'' ''9'' ''w''] entropy -> 2.75 100000 range entropy -> 16.6096404744
Functions marked hp are also found on the HP48 in a largely consistent format.
Program Flow Functions
-
repeat
- Takes objects X and n off the stack and evaluates object X repeatedly n times.X n repeat -> ... ''pig'' 3 repeat 3 2list -> LST:[TXT:pig, TXT:pig, TXT:pig]
Note that X is taken off the stack as is. If you try to put some kind of dynamic thing on the stack, it will be evaluated once and all the repeated iterations of it will be identical (i.e. the awake list will not be evaluated any more times). To have the sequence execute on every iteration, protect it.
|[rand 6 * 1 + floor::!] |roll_dice sto -> ** Empty Stack **
|roll_dice 5 repeat -> VAL:4.0 VAL:4.0 VAL:1.0 VAL:6.0 VAL:1.0
|[roll_dice::!] 5 repeat -> VAL:6.0 VAL:5.0 VAL:5.0 VAL:6.0 VAL:4.0
-
if
- Removes Xaction and then Xtest from the stack and if Xtest is true, evaluates the object Xaction.Xtest Xaction if -> ... |[rand 6 * 1 + floor::!] |roll_dice sto -> ** Empty Stack ** roll_dice roll_dice + 2 == ''snake eyes!'' if -> TXT:snake eyes! ''(1/36 of the time)'' #
-
ifelse
- Removes Xgood, Xbad, and Xtest from the stack and if Xtest is true, evaluates Xgood. If Xtest if false, Xbad is evaluated.Xtest Xgood Xbad ifelse -> ... rand .5 > ''heads'' ''tails'' ifelse -> TXT:tails (or heads)
-
while
- Xtest is removed from the stack once initially.Xtest Xaction while -> ...
Xaction is removed from the stack once initially. The loop process begins with the evaluation of Xtest. The resulting level one result is removed. If this result is true Xaction is executed. This is repeated until Xtest evaluates false. This means that when the loop should exit, an object should be placed on the stack by the action which will make the test evaluate false. For example:
13 |[dup 16 lt::!] |[dup ++::!] while -> 13 14 15 16
|[rand 6 * 1 + floor::!] |roll_dice sto -> ** Empty Stack **
roll_dice |[dup 5 le::!] |roll_dice while -> 3 3 1 3 6
-
gt
- Greater than. 1 if V2 is greater than V1, 0 if not.V2 V1 gt -> 1|0
-
lt
- Less than.V V lt -> 1|0
-
le
- Less than or equal to.V V ge -> 1|0
-
ge
- Greater than or equal to.V V ge -> 1|0
-
eq
- Equal to.V V eq -> 1|0
-
==
- Alias ofeq
. -
same
- hp Alias ofeq
. -
ne
- Alias ofeq not
. -
not
hp - Logical not.X not -> 0|1
-
and
hp - Logical and.X X and -> 0|1
-
nand
- Alias ofand not
. -
or
hp - Logical or.X X or -> 0|1
-
nor
- Alias ofor not
. -
xor
hp - Alias ofdup2 or 3 placen not swap not or and
. -
xnor
- Alias ofxor not
.
Flow Control
-
range
- Takes a positive integer off the stack and returns a list containing 0 through that number minus 1 in order. To get 1 through N, try something like this:4 range -> [0 1 2 3] 4 range 1 + -> [1 2 3 4]
-
repeat
-0 3 repeat -> 0 0 0
-
for
- Take two objects off the stack comprising of a list and an action object. For each item in the list, put that item on the stack and then evaluate the function object.OBlist OBaction for -> <action evaluated for all in list>
Notice that this is more like the Python for or the Perl foreach than the C for. If you want a C for loop such as:
for (i=20; i<50; i+=6) {leave_num_on_stack();}
try this:
20 |i sto |[i 50 <::!] |[i i 6 + |i sto::!] while
-
foreach
- Synonym forfor
. -
filter
- Take two objects off the stack, an object to perform some function on other objects and a list of those other objects. For each item in the list, put that item on the stack twice and then evaluate the function object. Then do an if. If the function object made a true value, then the copy will remain, if not, it goes. At the end, put all the stack items into a list except for the first n items where n is the size of the stack before the filter command or its arguments were introduced. In English, this means that if you have a stack with some original items, those should remain on the stack after the filter, but all of the objects the mapped function may produce will be rounded up into a list. Now the weird thing is that it is possible to have your filter function eat more levels off the stack than just the one of the list. In that case, you could have a stack be smaller than n+1 items where n is the number of original items. Example:1 |[dup 30 <::!] |[dup ++::!] while 0 2list |[7 mod not::!] filter -> LST:[VAL:7.0, VAL:14.0, VAL:21.0, VAL:28.0]
-
map
- Take two objects off the stack, an object to perform some function on other objects and a list of those other objects. For each item in the list, put that item on the stack and then evaluate the function object. At the end, put all the stack items into a list except for the first n items where n is the size of the stack before the map command or its arguments were introduced. In English, this means that if you have a stack with some original items, those should remain on the stack after the map, but all of the objects the mapped function may produce will be rounded up into a list. Now the weird thing is that it is possible to have your mapped function eat more levels off the stack than just the one of the list. In that case, you could have a stack be smaller than n+1 items where n is the number of original items.OBlist OBaction map -> [list of action applied to each in OBlist] |[25.4 *::!] |tomm sto -> 1 |[dup ++::!] 4 repeat 5 2list |tomm map -> [25.4 50.8 76.2 101.6 127.0]
Here’s a comparison of some different ways to make a random direction vector. All of these methods produce the same result.
|[rand .5 -::!] 3 repeat 3 2list
3 range |[drop rand .5 -::!] for 3 2list
3 range |[drop rand .5 -::!] map
[-.5] 3 :* |[rand +::!] map
Geometry Functions
Notes for commands to actually work with geometry. Woo!
Entity Construction
This basically makes a 3 item list from 3 numbers. The namespace is just a niceity and not required for anything.
1 2 3 pointform -> [1 2 3::x y z]
1 2 3 p -> [1 2 3::x y z]
Make a line entity. This adds a line entity to the model consisting of the two endpoints supplied. The entity ID of the line is returned as a numeric value.
[1 2 3] [5 6 7] line -> <entID>
1 2 3 p 5 6 7 p l -> <entID>
Make a tri entity in a similar way to how line
works.
[1 2 3] [5 6 7] [9 10 11] tri -> <entID>
1 2 3 p 5 6 7 p 9 10 11 p t -> <entID>
Here’s a nice demo of a triangle that I’ve been using.
10 10 0 p 100 10 0 p l 100 10 0 p 50 100 0 p l 50 100 0 p 10 10 0 p l
10 10 0 p 100 10 0 p 50 100 0 p t
View Control
The viewshow
function will print out all the parameters used by the
viewing system, including to2d
and the SVG/HTML output.
viewshow ->
vshow ->
The defaults look like this.
View Properties
---------------
SVGOUTFILE=/tmp/gg.html
CAMERA=0,0,1 {camset}
TARGET=0,0,0 {tarset}
OPACITY=True {hlr,hide}
FACELINES=False {facelines}
VLINEWIDTH=2.00 {vlw,viewlinewidth}
VREFRESHMS=1500 {refresh,viewrefreshms}
VBOX=(900,900) {viewbox[xy]}
VTRAN=(300,-450) {vtran[xy],viewtran[xy]}
VSCALE=(1,-1) {vscale[xy],viewscale[xy]}
Formerly there were commands such as camset
that set all these
variables. These were all replaced with a new system which uses
GeoGad’s own ability to store complex variables. When the system first
tries to produce a graphical output, it creates a view variable called
vw
which is structured like this.
[
''/tmp/gg.html''
[0.0 0.0 1.0::x y z]
[0.0 0.0 0.0::x y z]
1.0
0.0
2.0
1500.0
[900.0 900.0::x y]
[300.0 -450.0::x y]
[1.0 -1.0::x y]
::fi ca ta op fl lw ms bx tr sc]
This allows the existing variable syntax features to be used. For example, to check the existing value of and then set a new output file.
vw.fi ->
|vw.fi ''/tmp/snapshot.html'' sto ->
Note that these settings won’t appear to take effect immediately, but
the next time the display is updated, they should. You can used
refresh
to hasten that.
Here are the parameter names and their meaning.
-
vw.fi
- Output filename. This is where the HTML file is saved which allows the user to use a browser for an interactive display. -
vw.ca.x
- Camera X coordinate. Camera is where the viewer is looking from. -
vw.ca.y
- Camera Y coordinate. -
vw.ca.z
- Camera Z coordinate. -
vw.ta.x
- Target X coordinate. Where the viewer is looking to. -
vw.ta.y
- Target Y coordinate. -
vw.ta.z
- Target Z coordinate. -
vw.op
- Opacity flag. 0 is off. 1 is on. This controls whether the hidden line removal algorithm is activated when running the renderer. Think of it as a "wireframe" mode if set to 0. -
vw.fl
- Facelines. This controls whether trifaces will get 3 lines automatically rendered. This can be useful to find lost trifaces that become separated from their visible components or otherwise corrupt. 0, the default, is off, and 1 is on. -
vw.lw
- Line width. This is the width the lines are rendered by default. This is affected by scale and can be overridden with attribute tags. -
vw.ms
- Milliseconds. This specifies the reload delay encoded into the output HTML. The output HTML tries to reload itself after a certain time interval. This is where that is specified. The default is 1500 milliseconds or 1.5 seconds. -
vw.bx.x
- Output box X size. This is the width in pixels of the display area on the browser. -
vw.bx.y
- Output box Y size. This is the height in pixels of the display area on the browser. -
vw.tr.x
- Translate X value. This is the distance the model is moved horizontally (right generally being positive). This value is used to correctly position the display so that it is plotting the magnitude of values for the geometry you want to see. For example, if the display box is plotting model values of X between 50 and 100 and your model’s lowest X is -.5 and the highest is -.25, you will not see it. This parameter helps set the view port correctly. -
vw.tr.y
- Translate Y value. This is just like the X value but it is usually negative. This may be changed in the future so that the insanity of SVG’s top is 0, positive is down will be completely concealed. But right now if you want to move your model down in the display, use a bigger magnitude negative number. -
vw.sc.x
- Scale factor X. This controls how the display is scaled. If the view port is set up to plot 0 to 100 units in the X, and your model spans from -500 to 500, then you’ll need to change this by zooming out, i.e. a smaller scale factor. -
vw.sc.y
- Scale factor Y. Same as the X but, like the translate parameter, it is negative to account for the fact that SVG is upside down. If you use a positive value, the model will most likely be upside down.
To set the camera position. (The default is [0 0 1].)
2 -4 1 p |vw.ca sto ->
To set just the Y coordinate.
-4.5 |vw.ca.y sto ->
Query Entities
Easily getting at the most recent entity should be quite useful.
last -> <entID>
The order of entity creation should not be surprising. It should be possible to go back to arbitrary entities.
2 lastn -> <entID> # The penultimate entity.
It is often handy to do something with all of the entities. The all
function returns a list of all entities.
all -> [<entID1>...<entIDn>]
allent -> [<entID1>...<entIDn>]
Entity Tags
Keeping track of geometrical entities can be complicated. GeoGad allows for every entity to be given user defined tags. These tags can provide the functionality other programs achieve through groups, sets, layers, etc. Even entity attributes like "red" or "dashed" can reasonably be kept as tags allowing for very sophisticated management (see Attribute Tags).
The general syntax of the tag
function is an entity ID or list of
entity IDs is first supplied. Then a tag text item or a list of tag text
items. The text items are added to the tag list of each entity.
<entID> ''roof'' tag ->
[<entID> <entID> <entID>] ''roof'' tag ->
[<entID> <entID>] [''roof'' ''house''] tag ->
last ''doortop'' tag ->
all ''model'' tag ->
If you don’t want to lose the entity IDs from the stack, use dup
.
<entID> dup ''roof'' tag -> <entID>
100 0 0 p move ->
Tags can also be removed from an entity.
<entID> ''roof'' untag ->
[<entID> <entID>] [''roof'' ''house''] tag ->
Any specified entities which are tagged with any of the specified tags will have those tags removed.
To remove all tags from an entity or list of entities, use the detag
function.
<entID> detag ->
[<entID> <entID>] detag ->
Use all detag
to remove all tags in the model.
Often when copying entities, it is helpful to change one of the tags
to distinguish the old and new sets. To do this use the chtag
function.
<entID> ''old'' ''new'' untag ->
[<entID> <entID>] ''old'' ''new'' tag ->
Here is an example of copying all entities tagged with 'left' and tagging the new set with 'right' instead.
''left'' dup tag? edup swap ''right'' chtag ->
Querying By Tags
The tag?
function can filter on tags. Note that a list should always
be returned for consistency, even if it is empty.
''roof'' tag? -> [<entID> <entID>]
''goof'' tag? -> []
To find entities tagged by multiple tags, use a list. This will find any entities tagged with both "roof" and "house".
[''roof'' ''house''] tag? -> [<entID> <entID>]
This usage implies an and operation where all tags supplied must apply to the entity. To achieve an or effect use a construction like this.
[''roof'' ''house''] tag? [''roof'' ''barn''] tag? + -> [<entID> <entID>]
There is also a function for not tagged with the supplied tags. This means all of the tags must be absent if more than one are specified. For the simple case, this will produce a list of entities which do not have the "house" tag.
''house'' nottag? -> [<entID> <entID>]
The following will produce a list of entity IDs which are tagged with neither "roof" nor "house".
[''roof'' ''house''] nottag? -> [<entID> <entID>]
Attribute Tags
Attribute tags generally behave just like any tags, but they may have
additional magical properties relating to display rendering or other
indirect things. The format for an attribute tag is a single letter
followed by an @
. Using an @
anywhere else in a tag or for any
other reason is probably a bad idea. The syntax requires a letter
designating the type of attribute tag, then the @
designating it as
an attribute tag, and then the value of the attribute. For example,
C@red
would mean "color attribute red". Currently the supported
attributes are:
-
C
- Color can use any named color fromusr/share/X11/rgb.txt
such asC@orchid
or a web color likeC@#ce1337
or even something likeC@rgb(80%,30%,50%)
orC@rgb(255,200,144)
. The SVG property isstroke
. If there are spaces in the color name, just remove them; for example,darkorchid
seemed to work. The color can be set tonone
oroff
if you do not want the entity to display. -
W
- Specifies line width or weight. It probably is measured in pixels by default. It is normal to specify this with something likeW@3.5
. Values can be any of the goofy things that CSS allows likeW@2.5px
orW@1em
. The SVG property isstroke-width
. -
S
- This specifies how the line will be drawn if it is not continuous. This is for dotted or dashed lines. The value here is a comma separated list of values that mean S,NS,[S,NS,…] where S is the distance to stroke the line and NS is the distance to skip not stroking. For a moderate dotted line, this will be fineS@3,3
. For a dashed line,S@5,1.5
works. For a technical center line, useS@5,5,20,5
. The SVG property isstroke-dasharray
. -
O
- Opacity. This controls the alpha channel of the object. This can be in percent where 100% is completely opaque, such asO@100%
. It can also be specified as a ratio from 0 to 1 such asO@.25
. The SVG property isstroke-opacity
.
Note
|
When setting an attribute tag, if an attribute tag of the same
variety already exists for an entity, it is removed to accommodate the
new one. If, for example, you have an entity currently tagged
C@green and you set a tag of C@red , the C@green will be removed. |
Showing Memory Model Data
The mm
function is used to check the status of the memory model
which includes the tags for each entity. This shows entity ID, point
geometry and tag list for all entities in the memory model.
mm ->
The mmitem
function will dump the memory model only for the supplied
entity or entity list.
[<entID1>...<entIDn>] mmitem ->
Finding Duplicated Entities
If an entity is using all of the exact same points as another entity,
it is not very useful, especially if the user does not realize these
entities are lurking there. If you want to find if a given entity or
list of entities has any duplicates use the doubles
function.
[<entID>...<entID>] edup? -> [<entID>...<entID>]
<entID> edup? -> [<entID>...<entID>]
<entID> doubles -> [<entID>...<entID>]
This should show any other entities which share the entities' points geometrically. If you specify entities which are themselves duplicates, only the first one in the list will be searched for other duplicates. This looks like the following example.
gg--> [2 14] mmitem
2: Line [50.00 100.00 0.00] [10.00 10.00 0.00]
14: Line [50.00 100.00 0.00] [10.00 10.00 0.00]
** Empty Stack **
gg--> [2 14] doubles
/--------------------
| (1) LST:[VAL:14.0]
\--------------------
gg--> # [14 2] doubles
/--------------------
| (1) LST:[VAL:2.0]
\--------------------
To get rid of any duplicates you may have been unaware of, use all
doubles ~
.
Entity Decomposition
Get the geometry for a supplied <entID>.
<entID> pts -> <entPointLST>
22 pts -> [1 2 3::x y z] [5 6 7::x y z]
33 pts -> [1 2 3::x y z] [5 6 7::x y z] [9 10 11::x y z]
A list of entity of ids is also supported but the output is not in a list. This could be changed in the future.
[<entID1> ... <entIDn>] pts -> [ <entPointLST> ]
[1 2] pts -> [1 2 3::x y z] [5 6 7::x y z] [8 9 10::x y z] [11 12 13::x y z]
To get components of a list, it is often easiest to use the aliases
for getn
and putn
. If these are called with numbers to specify
position, then they will actually call get
and put
.
[9 10 11] 1 >> -> 9
To use with lists with named parts, you can specify the position with a symbol or a text.
11 22 33 pointform |x >> -> 11.0
11 22 33 pointform ''y'' >> -> 22.0
This for assignment.
[9 10 11] 5 2 << -> [9 5 11]
11 22 0 p |myp sto |myp |z 33 << myp -> [11.0 22.0 33.0]<x y z>
See get
, put
, getn
, and putn
in the list section.
Measurement
To find the distance between two points use the dist
command.
[0 0 0] [10 10 10] dist -> 17.3205
To find the length of a line first decompose it with the pts
command
like this.
3 pts dist -> <distance_VAL_of_entity_#3>
Angle measurement can be done with the angle
function.
Given 3 points (LSTs of three VALs) on stack level 3, 2 and 1, compute
the angle in degrees between the two supplied points of level 3 and 2,
and the two supplied points of level 2 and 1. For example:
[5 5 0] [0 0 0] [-5 5 5] angle -> 90
To get the angle between two line entities which share a common end point, use something like:
<entID1> pts <entID2> pts drop angle ->
If the line entities do not share an endpoint, wait until the intersection function is completed.
Midpoint
Compute midpoint of a line.
[1 2 3] [5 6 7] midp -> [3 4 5]
[1 2 3] [5 6 7] % -> [3 4 5]
Compute point some ratio along line.
[0 0 0] [8 8 8] .25 midpn -> [2 2 2]
[0 0 0] [8 8 8] .25 %n -> [2 2 2]
Entity Destruction
Erase an entity specified by entity id or a list of entity IDs.
<entID> erase ->
<entID> ~ ->
[<entID1>...<entIDn>] erase ->
If you want to erase all entities from the model use the all
command in conjunction with erase
.
all erase->
all ~ ->
Copy
The entdup
function takes the supplied entity ID and makes a copy of
that entity. The copied entity uses the exact same points (whose
reference count increases by one). The entity ID of the new entity is
returned to the stack. The edup
function is an alias of entdup
.
<entID> entdup -> <new_entID>
<entID> edup -> <new_entID>
This function can also take a list of entity IDs in which case the returned entity IDs are also in a list.
[<entID1>...<entIDn>] entdup -> [<new_entID1>...<new_entIDn>]
The set of all entities can be copied with the all
command.
all entdup -> [<new_entID1>...<new_entIDn>]
All tags an entity has are replicated in the duplicated entity. If
this is not wanted, use the detag
function.
<entID> edup dup detag -> <new_entID>
Move
Move an entity’s points by a supplied offset. This should politely not add new points if you’re doing something like moving p1 of a line to p2. In that case, p2 should remain untouched and what was p2 will get a new point if there isn’t one already in the model.
<entID> [x y z] move ->
<entID> [x y z] m ->
If a list of <entID> VAL is supplied with an offset, then each of the entities will be moved.
[ <entID>...<entID> ] [x y z] move ->
If you include the same entity more than once, then it is moved for each occurrence in the list
Rotate
Rotate one or more entities around a provided axis by a provided angle.
This function takes 3 items off the stack. In order of entry, the third
item is either an entity ID value or a list of one or more entity IDs.
These are the entities which will be modified by the rotation.
The second item is the axis of rotation. It can be an entity ID value
of a line entity (tri entities are not supported) or it can be a list
containing precisely two lists which each contain three values. This
list contains two coordinate lists for points on (defining) a line. This
line is the axis of rotation. Finally, the first item popped off the
stack (last entered) is the angle in degrees of the rotation. An example
is [2 4 13] [[0 0 0][0 0 1]] 45 rotate ->
, which would rotate entities
2, 4, and 13 by 45 degrees counter clockwise when seen looking from the
second reference axis point ([0 0 1]) to the first ([0 0 0]).
<entID> <line_entID> <angle> rotate ->
[<entID1>...<entIDn>] <line_entID> 45 rotate ->
<entID> [[5 5 0][5 5 1]] 45 rotate ->
[<entID1>...<entIDn>] [[5 5 0][5 5 1]] 45 rotate ->
File Operations
Sourcing
Load stack items/script from file as if typing it in.
''/home/xed/dodecahedron.gg'' source -> ...
Note
|
The source command can be used for any GeoGad commands. Any
kind of script or module you wish to prepare and deploy can be put in
a file as a sequence of commands and sourced in at any time. |
Memory Model Save
In theory there are very clever ways to do this, but for right now,
the system will output a file that can be used with source
to
roughly replicate the current geometry model in memory. It is not
exact because entity and point IDs can become reassigned, however, the
geometry should be exact. This saved file can also be edited in a
text editor to achieve desired effects.
mmsave ->
The file that is saved to is currently set to /tmp/savefile.gg
by
default. To save into a differently named file use mmsaveas
.
''/tmp/testmodel.gg'' mmsaveas
Subsequent calls to mmsave
will use the supplied file name after a
successful save with mmsaveas
.
Macros
Because there is a full programming language supporting the system, it is possible to create complex macros that do useful things. Here, for example, is a macro that will create a full 3d box (aligned to main axes) when given two points on opposite corners of the box. This will create a box with a corner at 0,0,0 and an opposite corner at 10,20,5. The entities comprising the box will be placed on the stack in a list.
0 0 0 p 10 20 5 p box -> [ <list_of_ents>]
''Function "Funbox"'' #
''Macro to create all the components of a box.'' #
''Includes 12 lines for edges, and 12 tris, 2 per side.'' #
|[->
P.x P.y Q.z p Q.x P.y P.z p dup2
P.x P.y P.z p t dup ''s1'' tag swap rot
P.x P.y P.z p t dup ''s1'' tag
P.x Q.y P.z p Q.x Q.y Q.z p dup2
P.x Q.y Q.z p t dup ''s2'' tag swap rot
Q.x Q.y P.z p t dup ''s2'' tag
P.x P.y Q.z p P.x Q.y P.z p dup2
P.x Q.y Q.z p t dup ''s3'' tag swap rot
P.x P.y P.z p t dup ''s3'' tag
Q.x P.y P.z p Q.x Q.y P.z p dup2
Q.x Q.y Q.z p t dup ''s4'' tag swap rot
Q.x P.y Q.z p t dup ''s4'' tag
Q.x P.y Q.z p P.x Q.y Q.z p dup2
P.x P.y Q.z p t dup ''s5'' tag swap rot
Q.x Q.y Q.z p t dup ''s5'' tag
P.x P.y P.z p Q.x Q.y P.z p dup2
Q.x P.y P.z p t dup ''s6'' tag swap rot
P.x Q.y P.z p t dup ''s6'' tag
P.x P.y P.z p dup
Q.x P.y P.z p dup rot l dup ''e1'' tag swap rot swap
Q.x P.y Q.z p dup rot l dup ''e2'' tag swap rot swap
P.x P.y Q.z p dup rot l dup ''e3'' tag swap rot swap
l dup ''e4'' tag swap
P.x Q.y P.z p dup
Q.x Q.y P.z p dup rot l dup ''e5'' tag swap rot swap
Q.x Q.y Q.z p dup rot l dup ''e6'' tag swap rot swap
P.x Q.y Q.z p dup rot l dup ''e7'' tag swap rot swap
l dup ''e8'' tag swap
P.x P.y P.z p P.x Q.y P.z p l dup ''e9'' tag
Q.x P.y P.z p Q.x Q.y P.z p l dup ''e10'' tag
Q.x P.y Q.z p Q.x Q.y Q.z p l dup ''e11'' tag
P.x P.y Q.z p P.x Q.y Q.z p l dup ''e12'' tag
24 2list
::! P Q] |box sto
TODO
Query Entities - TODO
Find entity by coordinates. Enter a point and get a list of all
entities which use this point. (hint EntityList.find_ents_by_coords()
)
What entities exist in some mathematical way at the provided point?
[10 10 0] esearch -> [ <entID> ... <entID> ]
[10 10 0] e? -> [ <entID> ... <entID> ]
What entities have a definition point matching the provided one?
[10 10 0] ep? -> [ <entID> ... <entID> ]
[10 10 0] epsearch -> [ <entID> ... <entID> ]
What entities have all definition points inside the provided bounding box?
[5 5 5] [12 12 12] ebbsearch -> [ <entID> ... <entID> ]
[5 5 5] [12 12 12] ebb? -> [ <entID> ... <entID> ]
What entities have some part inside the provided bounding box? This
uses the 8<
syntax which is like scissors representing a "cutting"
bounding box.
[5 5 5] [12 12 12] ebbsearch8< -> [ <entID> ... <entID> ]
[5 5 5] [12 12 12] ebb?8< -> [ <entID> ... <entID> ]
-
What about spherical nearness to a point?
-
What about within a cylindrical boundary (useful for catching things near a line)?
tube
-
What about entities that a specified line passes through?
spear
?laser
? When the line passes through just the points proper?
Scale - TODO
Look at listwise *
.
Scale an entity or a point evenly. Can this kind of polymorphism work?
<entID> .5 scale -> <entID> +SIDE_EFFECT:entID is now 1/2 size
[8 10 12] .5 scale -> [4 5 6]
<entID> [1 .5 2] scale -> <entID> +SIDE_EFFECT:entID scaled xyz
[8 10 12] [1 .5 2] scale -> [8 5 24]
(3) <entID> OR [<entID1> ... <entIDn>]
(2) [Xbase Ybase Zbase] or ???
(1) UniformFactor OR [Xfactor Yfactor Zfactor]
gg--> scale
Can mirroring be done with scale? Should be doable for simple mirrors on axes. Probably the rest of it can be done with a fancy macro. Need to think about mirroring.
[8 10 12] [-1 1 1] scale -> [-8 5 24]
Unimplemented Attributes
~@
"No render" attribute to make the equivalent of hidden layers.
Highlight attribute. This is for just highlighting an entity. Perhaps this could even trigger 3 lines for highlighted tris too.
@@
There are some other SVG properties that are not implemented. For reference, here are some of them.
-
stroke-linecap
-
stroke-dashoffset
-
fill - simple color specification for areas (maybe not helpful here)
-
stroke-linejoin (not useful here yet)
See SVG docs for inspiration.
In the future arrow end points might be explored, but they are not implemented now. Maybe it’s not even possible.
A@end A@start A@both A@-> A@<- A@<->
Viewing - TODO
Non model output - TODO
It should be possible to turn on modes where geometry which is not part of the model is sent to the rendering pipeline.
Display a grid. Maybe there could be a grid spacing setting.
grid ->
VAL gridspacing ->
Draw a representation of the origin and +x, +y, and +z or the 3 axis in general.
xyzicon ->
axes ->
Simply puts an SVG point marker at this place for highlighting and reference only. The model is unaffected. Disappears on next render. Note that the input point is not dropped so that it’s easy to check if the point on the stack is the one you’re interested in.
[10 1.5 -3] refpt -> [10 1.5 -3]
Draw a 3d bounding box for reference.
[0 0 0] [10 10 10] refbb ->
Draw a reference bounding box of the model’s extents.
[0 0 0] [10 10 10] refextbb ->
Highlight entity. Take an entity or list of entities and give them a special highlighted rendering. Don’t drop them from the stack so this can be used to verify you’re working with the entities you intend to.
<entID> highlight -> <entID>
<entID> ; -> <entID>
[<entID1> ... <entIDn>] ; -> [<entID1> ... <entIDn>]
[<entID1> ... <entIDn>] highlight -> [<entID1> ... <entIDn>]
Variables that exist now - TODO
Set shallow angle cull value.
12 shallow ->
Set roll angle (to2d not ready).
30 roll ->
Set aspect (to2d not ready).
.75 aspect ->
Set zoom factor (to2d not read).
10 zoom ->
Reference For to2d
to2d - Chris X Edwards
-a Aspect ratio <ratio>. NOT DONE!
+c Camera point <x,y,z>.
-d Deleted lines visible <color>.
+f Faces only, fill-in lines.
*h This help message.
+o Opacity flag. Hidden line removal.
-p Pierce point marks <size>.
-r Camera roll aka twist <angle>.
-s Shallow surface cull <angle>.
+t Target point <x,y,z>.
-z Zoom factor <factor>. NOT DONE!
Bugs
-
[]
produces[None]
. Need to handle that much better. Contrast withtag?
with a nonexistent tag. -
Tons of input validation to do! There should not be catastrophic failure when there are not enough items on the stack.
-
Recursive naughtiness:
RuntimeError: maximum recursion depth exceeded in cmp
Or is that correct/reasonable behavior? Maybe at least trap it.[ dang 1 2 ] |dang sto dang wake ! -> ** RuntimeError **
To Do
-
! Work on adding
zoom
andpan
, especially zoom extents. Involves better calculations ofvw.tr
andvw.sc
. Maybe assume SVG should be Y inverted too. -
Create view angle commands
azi
(azimuth or bearing) andalt
(altitude, 90-zenith) that can setvw.ca
using angular coordinates. This would allow the user to orbit the model more predictably. -
Change current simplistic
mmsave
tommggout
or something like that (maybemmdump
after SQL engines). Make a new and improvedmmsave
that really optimally saves the model. Needs to be efficient for saving/loading time and an saving space. Also should save session state properties (likevw
) and anything else there might be. Does not need to be human readable. Need anmmload
to go with this. -
The ability to save arbitrary parts of a model would be good. Perhaps provide items, a file name, and
itemsave
. Thenitemload
to load in those entities. -
Improve
to2d
. -
Convert
VAL
toV
,LST
toL
,TXT
toT
,SYM
toS
in output strings. -
Limit huge items on stack output display (maybe toggle
verbose
setting to display everything or polite)(1) LST:[VAL:0.0, VAL:1.0, VAL:2.0, VAL:3.0, VAL:4.0, VAL:5.0, VAL:6.0, VAL:7.0, VAL:8.0, VAL:9.0, VAL:10.0, VAL:11.0, .......+359
-
Define StackOB_LST of
argv
. -
Define functions to adjust settings (verbosity, special chars, etc.)
-
Better organized help - help by concept.
-
time
,date
,sleep
-
cd
,ls
,file_read
,file_write
,file_append
-
Suppress stack display for each operation during a
source
. -
Polar move… Hmmm, what about the 3rd dimension?
-
Lining Tris. Often it is useful to have a tri entity with three matching line entities connecting its vertices.
trilines
? It might also be good to have a plausible process to compose a tri from 3 connected lines, i.e. 3 line entities which only reference 3 total points. -
Macros for Arrays (grids, bolt circles, etc).
-
Macros for circles, arcs, discs, ellipses, cylinders, tubes, cones, etc. Start thinking of best 1st class entities for such things.
-
Macros for multi-segmented lines. Think about 1st class entities for such things. Also splines and friends.
-
Think about 1st class entity for Text (which is damn easy in SVG).
-
Think about 1st class entity for point markers.
-
Macros for holes and cutouts.
-
Extrusions? Along an arbitrary vector (Ahem ACAD).
-
Trim and extend? Don’t know how exactly to style those but they’re handy.
-
Intersection. Compute point where two lines intersect. Also for non intersecting lines, calculate intersection of the lines' projections (shadows). Once this is done, use it in conjunction with the
angle
function to work out a method to calculate angles between two lines which do not share an endpoint. Or something like that. -
tee
Point from 1 perpendicular to 2-3.[0 0 0] [8 8 0] [8 0 0] tee -> [4 4 0]
-
Area measurements? Plausible? Volume?
[[0 0 0] [10 10 10] [-5 0 5]] area -> 25
-
What about groups of groups? As long as all sublists contains LST or VALs, should be ok to traverse recursively. Need to have a better (recursive) function for the
filter
andmap
commands that check things. -
Listwise operation:
+
,-
,*
,/
,--
,++
,sin
,cos
,ceil
,inv
,not
,or
,and
,sqrt
,tan
,pow
,neg
,abs
,mod
. Maybe more. Double check++
and--
which are currently a bit odd. -
+
and*
are good. Check other operators for correct behavior.
[4 8] 4 :- -> [8]
4 [4 8 4] :- -> [8 4]
[4 8 4] 4 :- -> [4 8]
[4] [1 2 3] - -> [3 2 3]
[4 5 6] [0 3] - -> [4 2 6]
-
Vector math! Add, subtract, dot, cross, inverse, length, angles, normalize. Note if the namespaces are the same, preserve. Or maybe preserve even if present in only one term and the list lengths are the same.
-
Extrema. From the points supplied, return the point with the largest or smallest X, Y, or Z. Not even sure this is a good idea.
-
Get
undo
command working withfn_mm
. This is important but hard. Actually most of it seems intact but each function needs to have its undo compliment worked out. Maybe an automated way to do that? -
PostScript output. Should not be too difficult.
-
Or even more badass, review my
...prog/c/Pro2SD/output_gnuplot.cc
-
CGI. Everybody’s doing it. (clara.io)
-
WebGL.
-
TK, OpenGL.
Ideas From The HP48
List Operations
-
GETI - Does the same thing as
get
but returns the original list and the position value incremented by 1 before returning the value of the actualget
operation. This allows it to be called repeatedly and all list members can be selected. A more modern way to do this might be... map wake !
-
PUTI - Same dynamics as GETI. Not sure this is needed.
Stack
-
TYPE - Returns what kind of type an object is. (O.whatami)
-
→STR - Converts object to TXT object form.
-
STR→ - Evaluates text of string as if it were commands. Maybe
wake
should do this. -
LAST - Places the same arguments of the most recently executed command on the stack as they were before the command was executed. This does not undo the last operation. Any changed objects will still be changed. Note that this conflicts with
last
which returns the last generated entity’s ID. That usage will probably stay.
Statistics
-
BINS - Sort into frequency bins.
-
UTPC - Upper Chi-Square Distribution
-
UTPF - Upper Snedecor’s F Distribution
-
UTPN - Upper Normal Distribution
-
UTPT - Upper Student’s t Distribution
Vector
-
CROSS
-
DOT
Time and Date
-
DATE
-
DATE+
-
DDATS
-
TIME
File
-
PATH
Run
-
INPUT
-
PROMPT
-
DBUG
-
SST - Single step.
-
NEXT
Math
-
SIGN
-
STO-, STO+, STO/, STO--, STO+ +, STO* - Note that the order shouldn’t matter as long as there is one sym and one value.
v2 v1 sto*
is equivalent to:SYM VAL STO* -> swap dup ! rot * swap sto VAL SYM STO* -> dup ! rot * swap sto