KHEPRI.GH
About the project
Algorithmic approaches are currently being introduced in many areas of
human activity and architecture is no exception. However, designing with
algorithms is a foreign concept to many and the inadequacy of current
programming environments creates a barrier to the generalized adoption of
Algorithmic Design (AD). The Khepri.gh project aims to provide architects
with a programming tool they feel comfortable with, while allowing them to
fully benefit from AD's advantages in the creation of complex architectural
models.
Khepri.gh is a hybrid solution that combines Grasshopper, a visual
programming environment, with Khepri, a flexible and scalable textual
programming tool. Khepri.gh establishes a bridge between the visual and the
textual paradigm, offering its users the best of both worlds while providing
an extra set of advantages, including portability among CAD, BIM, and
analysis tools.
Tutorial presented at the 2020 CAADRIA Conference
Features
Installation Instructions
✔ Download the relevant files.
You should have 4 files as shown in the image.
✔ Make sure the files are unblocked: rigth click > properties >
unblock.
✔ Drag the plug-ins onto Grasshopper's Component Folder:
in Grasshopper open File > Special Folders > Components
Folder or
in Windows's go to C:\Users\<user
name>\AppData\Roaming\Grasshopper\Libraries
✔ Khepri's basic component should appear in Grasshopper's
Math tab. Look for Khepri's logo.
Basic Use Instructions
Create Khepri components
Go to Math tab > Script and drag the bug (Khepri icon) onto the
canvas and double click it to edit.
Alternatively, double click the
canvas and use the search menu to find Khepri.
Save Khepri components
Select the component you want to save and go to File > Create User Object (note: make sure the Hide Object option is unchecked). You may change the name, nickname, description, and icon if you want. Choose a Category tab or create a new one. The saved components will be available in that tab afterwards.
Symbology within components
<
is used to define component's input variables
>
is used to define the component's output variables
=
is used to define variables within the component
much like in Julia
Anything to the left of these symbols will be interpreted by
Julia as variable names, which can be used only within the scope of the
particular component they are defined in (exception made to the
Definitions Component, which we will be explained later on).
To the right of the symbol, Khepri.gh expects you to provide a
TypeFunction with the information required to identify (1) the
type of data being handled and (2) the information Grasshopper needs on
the names and descriptions attributed to each of the component's inputs
and outputs.
Note: If you intend to output a value/expression that is not a
variable within the component, you can directly provide the corresponding
value/expression to the output symbol. You may also use
_
before the output symbol, thus providing no explicit
expression for the output. This will be considered an anonymous variable
and the component will output the result of the last computation made
within it. Only works properly for one output.
Abstract examples:
variable_a < VariableType()
is an input.
variable_b = <variable value/expression>
can be used
within the component.
(variable_c,variable_d) = <variable value(s)/expression(s)>
exemplifies a definition using tuples.
variable_b > VariableType()
is an output.
<variable value/expression> > VariableType()
is a
direct output with no variable name associated to it within the
component.
_ > VariableType()
is an output with no expression provided
directly. The result of the last computation made within the component
will be outputted.
Typical Component Layout
# Title
# v < Type("Input", "I", "I Parameter", default)
a < Number()
b < Number()
_ > Number()
sqrt(a^2 + b^2)
The first line will be used as the component's title by default. You may
ignore this and edit the title later by double clicking on the component and
changing the name field. If you want to define the title in the editor using the first line, do
not forget to comment it (using #
) since within the component all characters
are read as code.
v
exemplifies the information the user is supposed to provide
to the TypeFunction: name, nickname, description, and default value.
None of these arguments are mandatory. If no arguments are provided, the
system will use defaults for everything. If only the name argument is
provided, the system will infer the nickname (capitalized first letter of
the name), the description ("<nickname> parameter"), and the default
value, as exemplified below with parameters a, b, and _. Users may also
provide only one numeric/default argument corresponding to the
initialization value.
The order of input/output is irrelevant. Use normal Julia programming order. Note b's imperative assignment in the following example.
Examples for TypeFunction arguments:
p < Point("Sphere Center", "P", "P defines the center of the
sphere")
pts > Points("pts_down_curve", "PsC", "List of GH locations for Curve")
Supported Variable Types
Type Function:
Type(name, Nickname, Description, Default)
Items:
Integer(name, Nickname=name[1:1], Description=name, Default=0)
Number(name, Nickname=name[1:1], Description=name, Default=0)
Point(name, Nickname=name[1:1], Description=name, Default=u0())
Vector(name, Nickname=name[1:1], Description=name, Default=vx(1))
Boolean(name, Nickname=name[1:1], Description=name, Default=true)
String(name, Nickname=name[1:1], Description=name, Default="")
Path(name, Nickname=name[1:1], Description=name, Default="")
Any(name, Nickname=name[1:1], Description=name, Default=nothing)
JL(name, Nickname=name[1:1], Description=name, Default=nothing)
Eval(name, Nickname=name[1:1], Description=name, Default="")
Lists:
Integers(name, Nickname=name[1:1], Description=name, Default=[])
Numbers(name, Nickname=name[1:1], Description=name, Default=[])
Points(name, Nickname=name[1:1], Description=name, Default=[])
Vectors(name, Nickname=name[1:1], Description=name, Default=[])
Booleans(name, Nickname=name[1:1], Description=name, Default=[])
Strings(name, Nickname=name[1:1], Description=name, Default=[])
Paths(name, Nickname=name[1:1], Description=name, Default=[])
Many(name, Nickname=name[1:1], Description=name, Default=[])
JLs(name, Nickname=name[1:1], Description=name, Default=[])
Evals(name, Nickname=name[1:1], Description=name, Default=[])
Lists of Lists (Trees):
Integerss(name, Nickname=name[1:1], Description=name, Default=[])
Numberss(name, Nickname=name[1:1], Description=name, Default=[])
Pointss(name, Nickname=name[1:1], Description=name, Default=[])
Vectorss(name, Nickname=name[1:1], Description=name, Default=[])
Booleanss(name, Nickname=name[1:1], Description=name, Default=[])
Stringss(name, Nickname=name[1:1], Description=name, Default=[])
Pathss(name, Nickname=name[1:1], Description=name, Default=[])
Manies(name, Nickname=name[1:1], Description=name, Default=[])
JLss(name, Nickname=name[1:1], Description=name, Default=[])
Evalss(name, Nickname=name[1:1], Description=name, Default=[])
Variable Types explained
Number(s(s)), Integer(s(s)), Point(s(s)), Vector(s(s)),
String(s(s)), Boolean(s(s)), Path(s(s))
For the 7 basic types there is a direct correspondence
between Julia and Grasshopper. This means you can input and output them
at will, interchanging data between the two programming languages,
whilst Khepri automatically converts from one end to the other as
needed.
Any/Many/Manies
To output the result of Julia calculation within a
Khepri component for Grasshopper, you can use any of 7 basic types:
the output can be used to feed Grasshopper components as Khepri
covers everything in the background.
If you do not know what the result of the computation will be
yet (could be one of multiple types depending on the input), choose
the Any/Many type instead. Khepri will dynamically try to match the
type of the result to any of the 7 basic types. If it fails
(meaning, none of the types apply) it will simply return a pointer
(much like the result of the JL type).
⚠ This type cannot be initialized within the
component, meaning it cannot have a default.
Eval(s(s))
Eval deals with any Julia expression provided as a
string - these may come from a Grasshopper panel (no quotation marks
needed! The panel produces a string) or from another Khepri
component (with eval/evals as an output). Eval evaluates the given
expression as Julia code. The result will be a value in Julia.
⚠ Code written in components is more efficient
than using panels and Eval types (although the latter is more
visually appealing), because everything is compiled on a first read.
Panels being inputted onto an Eval must recompile
all the time. this is a time vs. beauty tradeoff.
JL(s(s))
Data types only Julia can process. There is no correspondent
representation in Grasshopper. Hence, Khepri will return a pointer (memory).
You may visualize the pointer using a Grasshopper panel, but you may not
operate on it using Grasshopper components, only Khepri's. Examples
include functions, shapes, etc.
Definitions Component
Components with no inputs or outputs are treated differently (like text files) - use them for definitions only, no geometry generation. This one forces the recomputation of all the others, because it does not know which other components depend on it. The other types of components only force recomputation on their node/wire chain.
Grasshopper vs Julia iterations
Singular (item) variables can also receive plural (list) inputs. In this
case. See Example 1 in the image below. In this case, Grasshopper itself
iterates over the inputs one by one, running the component multiple times.
In this case, adding 1 to each item of the list at a time.
In Example 2 we force Julia to make the iteration instead by
specifying the plurality of the input. In this case the component runs only
once as the input is swallowed altogether the first time. Only like this can
Julia retain the information of the inputs for computations within the
component. In this case, we sum all the elements of the list.
The same sum cannot be achieved if Grasshopper is left in charge of
the iteration. As visible in Example 3, the component runs 3 times, once for
each item, performing a useless sum for each and never arriving at the total
of 12.
The following image shows the sum example with a tree for input. Instead of Eval, we require the Evalss type to swallow this input as whole. Like so, Julia is in charge of the entire process, the component runs only one, and the input is treated as a list os lists. Hence we need to use the broadcast operator in Julia (.) to iterate over the list, mapping the sum operation to both sub-lists.
General Examples
Two direct outputs with no associated variable name:
Plural outputs (lists):
Eval:
Evals - use Grasshopper's multiple data panels for multiple Eval entries, i.e. plural outputs/lists:
Strings:
Many/Manies:
Jls:
Tree Types (lists of lists):