| DOSE: |
14 mg |
oral |
2C-E |
(powder / crystals) |
It's two o'clock in the afternoon and I am lost in an endless, ancient forest. An hour ago I wandered around the same area, noticing the roads and bike paths cutting their way through the orderly stands of planted trees. The paths were full of people bicycling or walking their dogs. They are probably still around right now, but I don't notice them. I am watching a sea of pinecones.
Forty-five minutes ago my boyfriend and I swallowed 14mg of 2C-E dissolved in fruit juice and headed off into the woods. This weekend we are at an ambient music festival being held in a forest amphitheater. The music seems to follow us wherever we walk, and we joke about having our own movie soundtrack. This is the second time we have tried 2C-E. The previous experience was very pleasant despite persistent nausea, and we have been looking forward to this afternoon.
Ten minutes ago I was trying to read an information board along a path and noticed that I couldn't focus on the words. The harder I tried to read it, the more impossible the task became. I started to laugh, but then I noticed an older couple approaching. The nearby village is populated mainly by wealthy, conservative retirees, and I felt ashamed for acting strangely and disturbing their daily walk. We quickly moved on, deeper into the forest.
During the last few minutes the trees have grown taller and older, and I am back in the forests I knew as a child. Walking over the top of a small hill, we are suddenly surrounded by a stand of pine trees reaching improbably far into the sky. Sunlight streams through the canopies in tangible ropes of golden light. I can feel them as a weight on my shoulders. We are in a cathedral more magnificent than any in Europe, but everyone else rushes by on their walk and doesn't notice. The ground is littered with thousands of small pinecones. I am certain that I have gained the ability to focus on them all at the same time, and we sit down to look at them for several minutes.
...
The next path is narrow, winding its way between trees and undergrowth. The remembered sensations of childhood return, and I feel like I am once again seven years old, running through the forest behind my grandparents' house on trails known only to me. A nearby flutter catches my attention and I turn to see a robin sitting in a tree just a meter away. Although it is dark in this part of the forest, I can see the robin in crisp, colored detail. Everything around it has smeared into a featureless grey background. I watch the robin. The robin watches me. It is magnified - the structure of each feather is clear, and I can see it breathing. We watch each other until Z grabs my arm to tell me something. I startle and the robin vanishes into the forest.
The forest thins and the pine needle carpet is replaced by fine sand, shining brightly in the afternoon sunlight. I can tell that I should be feeling hot walking on the sand, but my body is not registering temperature any longer. We head back to the forest edge to sit in the shade. My eyes track every insect along the way. A large dragonfly hovers in midair in front of me, and I see in slow motion the wings tracing a figure-eight in the air around the metallic green and gold body. Smaller insects compete for my attention, scuttling through the sand all around me in eye-blinding colors. If I focus on them all, I can see the entire sandy path wriggling with life.
...
Four forty. Edges of objects glow and ripple. When I look at my skin, it first turns wrinkly and warty, and then transforms into tree bark. Mazes and tribal patterns spread across Z's face when I look at him. It is difficult to watch - some part of me does not like seeing this alien version of him. We have been talking all afternoon, about life, work, friends, and what we are experiencing, but now I am having a hard time putting my thoughts into words. Complex ideas are being lost halfway through their explanation, and I decide to wait until tomorrow to finish parts of the discussion. I have a small notebook with me, but I don't feel like writing down more than basic timing information. Z says that he keeps seeing things in his peripheral vision that aren't there when he turns to look at them.
...
It is just after six o'clock, and we are supposed to meet a friend at the parking lot on the other side of a lightly used local highway. The cars move treacherously, sometimes barely advancing, sometimes racing by. I know their speed is constant, but I can't judge it with any accuracy. We realize that we can no longer decide when to cross safely, and that the only option is to wait until there are no cars visible. Other people cross back and forth, looking puzzled at our indecision. Our friend arrives, and waves at us from the opposite side. When we refuse to walk across she hurries over. After we offer a contorted explanation she laughs bemusedly at our condition. Should I really be out in public like this?
We head for the amphitheater. I am uncomfortable near the festival, sure that everyone knows (and cares) what I have done. We pass stands selling food and drinks, but the smells make me nauseous. I sit down to listen to the music and wait for the others. Z returns with a plate of fruit slices, and we are soon surrounded by a small cloud of wasps.  He points at it and asks me 'Is this one real?', which elicits a giggle from the people sitting behind us.
...
At half past eight, the clouds are still rearranging themselves in a kaleidoscopic dance as we attempt to tell our friend about our experiences today. Speaking is not as tiring or difficult as it was several hours ago.
...
By ten o'clock we are back at the amphitheater. Now that it is dark, I no longer feel watched by everyone I pass.  Nausea is being replaced by a dull but insistent stomachache. Something is wrong with my sense of balance - I stumble around and need to occasionally grab onto handholds. The images projected onto the trees and amphitheater swirl only slightly. The visual effects are beginning to wear off. The music still sounds lovely, however, and I spend several hours listening to it and contemplating my life. Emotionally, this stuff is flat. No elation, no depression - just space to think in.
...
Nearly one o'clock in the morning. The visuals are gone, but some mental cloudiness is still present when I try to communicate. The stomachache has worsened, and is overshadowing any remaining positive effects. I'm ready for this to be over, and return to the campsite. Luckily, sleep comes easily, and the stomacheache is gone when I wake up.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


Alex Scott pays tribute to Neil Jones as Strictly injury doubts linger
Alex Scott has paid tribute to partner Neil Jones as his fitness hangs in the balance.
The professional dancer still does not know if he can return to the floor in the next Strictly Come Dancing show.
Jones is still waiting to get the green light from the physio to see if he can compete.
Scott, who has paired with Kevin Clifton for her previous two performances, has paid tribute to the injured dancer.
She said that no matter who she danced with, both Strictly professionals are “great guys”.
Speaking on Strictly: It Takes Two, the former Arsenal payer said: “If he’s 100% I know he will go out and dance.
“This guy is amazing and I wouldn’t be at the level that I’m at without him.”
Jones is waiting for a fitness test to see if he can rejoin Scott on the dance floor.
He said: “I’m really hoping. I still don’t know.
“I’ve been dancing today a lot, I’ve been doing that, it’s been feeling really good.
“But I need to be passed by the physio.”
Scott said she did not mind who she danced with in the next show, adding: “Whether I’m dancing with Neil or dancing with Kev, they’re both amazing guys.”
Strictly returns on Saturday night.


Wash the chicken and pat dry.
Strip the leaves from half the rosemary and thyme stalks and finely chop.
In a bowl, combine the lemon zest, chopped rosemary and thyme, pancetta and mascarpone. Season to taste with salt and freshly ground black pepper.
Halve the zested lemons and stuff into the chicken's cavity, along with the remaining herb sprigs.
 Push the mascarpone mixture under the breast skin to create a thick, even layer.
Put the onion slices into a large, flameproof roasting tin. Place the chicken on top and cover loosely with foil.
Cook for 1½-2 hours, basting every 30 minutes, until the juices of the bird run clear when the thigh is pierced with a skewer. If necessary, uncover for the last 30 minutes to brown the bird.
When cooked, take the chicken from the oven, remove from the roasting tin and leave to rest on a platter, covered loosely with foil, for 30 minutes.
To make the gravy, pour away any excess fat from the roasting tin and discard the onion slices.
Set in the roasting tin over a medium heat, then sprinkle over the flour. Stir through and cook for two minutes.
Slowly whisk in the wine and stock.
Bring to the boil, scraping the bits from the bottom of the roasting tin, then simmer over a low heat for 15 minutes.


MultiselectJS Tutorial
Table of Contents
1 Introduction
MultiselectJS is a library for implementing multi-selection, that is, the feature supporting selecting and deselecting elements from a collection using the mouse (or another pointing device) or a keyboard. The visual aspects of selection, the shape and location of elements, their ordering, indicators of selection status, etc. vary from one application to another. These are the aspects that the client defines, MultiselectJS implements the rest.
1.1 Concepts
The multi-selection task is to identify a subset of a
collection of elements. To abstract over what these elements are (DOM
elements, characters in text, polygons drawn on a canvas, and so forth), we
assume that each element is uniquely identified by some
index. Indices can be of any type that can be compared for equality
with ===
, such as numbers, object references, or strings.
The selection state of elements is modeled as a mapping from indices
to booleans, where true
indicates that an element is selected,
false
that it is not.  User’s selection actions, such as clicking the mouse on an
element, dragging a ``rubber band’’ around elements, or pressing an
arrow key with the shift modifier key held down translate to one or
more selection operations that modify the selection mapping.
Each selection operation is associated with a selection domain that determines the set of indices that the operation affects, and a selection function that determines whether the indices will be selected, deselected, or toggled. The user indicates the selection domain through specifying a selection path, a sequence of points in some suitable coordinate space. This selection space could be, for example, the mouse locations in a window or pairs of row and column indices in a grid of elements. The first point of a selection path arises from a click or a command-click1 and the subsequent points from shift-clicks (or mouse moves, when rubber band selecting). The first point is called the anchor and the last the active end of the path. In the case of a one-element path, the active end coincides with the anchor. The selection domain specified by the current selection path is the active selection domain.
Figure 1 shows concrete instances of the above concepts. The selectable elements are rectangles of arbitrary size, they are placed in arbitrary locations, and they can overlap. We make the following observations:
- The selected elements are items 2, 4, 5, and 6, and hence the
selection mapping maps the indices 2, 4, 5, and 6 to
true
, and all other indices tofalse
. - The element 4 has been selected with a prior selection command. To select the elements 2, 5, and 6, the user has clicked the location marked with a red circle, and then dragged the mouse (rubber band selection) through several other locations (small blue dots). The last, and current, mouse location is marked with a blue circle.
- The selection space is the space of mouse coordinates. The sequence of points indicated by the blue dots therefore constitute the selection path.
- In most selection contexts, the anchor and active end are the only points that matter in determining the selection domain. Here, the anchor and active end serve as the opposite corners of a rectangle—all elements that overlap with this rectangle (shown in blue) belong to the active selection domain.
Figure 1: A snapshot of multi-selection interaction. The selection path (the blue points) gives rise to the selection domain consisting of the items 2, 5 and 6.
How the selection path determines the selection domain varies from one context to another. This variation is captured by the selection geometry. Concretely, a selection geometry in MultiselectJS is an object that defines the functions:
m2v(point)
that converts mouse coordinates to selection space coordinates;selectionDomain(path, J)
that maps a selectionpath
to a selection domain (theJ
parameter is to enable optimizations to be explained later);extendPath(path, point)
that defines how a new point is added to the selection path;filter(pred)
that computes a selection domain as the set of indices that satisfy the predicatepred
;step(direction, point)
that defines how arrow keys should impact the current keyboard cursor location; anddefaultCursor(direction)
that defines default cursor locations for when keyboard cursor has not yet been established.
The library has default definitions for each of the selection
geometry’s functions, and often it suffices to implement only a subset
of them. For example, in the selection geometry of Figure
1, the default definitions for m2v
(identity function) and extendPath
(add point as the new anchor) can
be used. If the example does not support keyboard selection, step
and defaultCursor
need not be defined. If it does not support
selection by a predicate, filter
need not be defined. The only
function that must be defined is selectionDomain
; it computes the
indices of the elements that overlap with the rectangle indicated by
the anchor and active end.
Figure 2: The selection path is the sequence 5, 2. The anchor is 5 and the active end 2. The selection domain is the set {2, 3, 4, 5}.
Figure 2 shows a snapshot from a
selection context that has a different selection geometry. In this
geometry the selection space coincides with the set of element
indices: the m2v
function maps all mouse positions that fall within
an element’s extents to the index of that element. In this context,
elements are considered to be ordered, so the selectionDomain
function maps a selection path to the range of indices between
(inclusive) the path’s anchor and active end. The anchor is marked
with a red dashed frame and the active end with blue. Here, the user
has clicked first somewhere on Item 5
and then shift-clicked
somewhere on Item 2
. As a result, all elements between these two
items are marked selected.
Another aspect that varies from one selection context to another is if and how the anchor and the active end, and more generally the selection path, are visualized. MultiselectJS leaves these questions to the client, but makes the data needed for those visualizations readily available (see Section 3.7).
1.2 The meaning of click, command-click, and shift-click
Click, command-click, and shift-click are the basic selection tools that most applications support. Different applications assign slightly different meanings to these operations. The key bindings may vary as well (e.g., Windows’ ctrl-click corresponds to OS X’s command-click). These three commands are the basic building blocks of MultiselectJS, in terms of which most other (keyboard and rubber band selection) commands are defined. In a nutshell, the three selection commands work as follows:
- Click deselects all selected elements. The clicked point becomes the anchor and the sole element of the selection path. The new active selection domain is computed from this selection path; the elements in this domain are selected. The selection function is set to select, so that subsequent shift-click operations will select, rather than deselect, elements.
- The clicked point becomes the anchor and the sole element of the selection path. The new active selection domain is computed from this selection path. If the anchor is on an already selected element, the selection function is set to deselect, otherwise to select. The elements of the active selection domain are either selected or deselected according to the selection function.
- Shift-click extends the current selection path with a new point. It computes a new selection domain that corresponds to this selection path. The new selection domain replaces the current active selection domain.
MultiselectJS does not insist on particular key bindings for any of the selection operations, but the naming of the functions in its public API reflects our recommendations.
2 Example: selecting from an ordered list of non-overlapping elements
The first example is a horizontal list of elements, in which elements can be selected using the click, command-click, and shift-click commands. The Show animals button displays a list of currently selected elements.
| pig | cow | goat | horse | sheep | chicken | duck | turkey | ostrich | mule |
The example and its complete source code can be viewed in separate windows.
The implementation of this example consists of the following:
- Importing the MultiselectJS library.
- Defining the selectable elements.
- Defining a refresh function that visualizes the selection state of the elements.
- Defining a selection geometry object.
- Constructing a
SelectionState
object that maintains all of the selection state. - Defining mouse event handlers to call appropriate functions of
SelectionState
.
We explain each of the steps below.
2.1 Imports
To use MultiselectJS on a page is a matter of importing it as a script. There are no dependencies; we import jQuery because we use it in this tutorial.

2.1.1 multiselect.js
as a module
Alternatively, multiselect.js
is provided as a (CommonJS) module.
It can be installed locally as so:
npm install git+https://github.com/hotdrink/multiselectjs.git
and then used in a project like this:
var multiselect = require("multiselect");
Note that MultiselectJS uses ES6 features.
2.2 Selectable elements
In this example, the selectable elements are HTML table cells. We
give the cells the selectable
class attribute so that they are
easily accessible. The animal_list
span is a placeholder for where
the selected animal names will be shown when the show_animals
button
is clicked.

pig	 cow	 goat	 horse	 sheep	 chicken	 duck	 turkey	 ostrich	 mule

 <button id="show_animals">Show selected animals</button>
Next, we access the above HTML elements from JavaScript code:
var selectableArea = document.getElementById("selectable_area"); var selectables = selectableArea.getElementsByClassName("selectable");
The selectableArea
object is the target of the mouse events.
The selectables
objects is the collection of the selectable elements;
it is an “array-like” object, indexed with integers.
2.3 Visualizing the selection state
The following CSS code defines the visual appearance of selectable
elements in both their unselected and selected states.
The .selected
class is turned on when an element is selected and off
when deselected.

To display the current selection state, MultiselectJS invokes a
callback function after every selection command (unless the library
recognizes that a command had no effect), passing it the current selection mapping.
We use the refresh(s)
function below as the callback; it iterates
over all selectable elements and toggles the selected
class
attribute according to each element’s selection status:
function refresh(s) { for(var i=0; i<selectables.length; ++i) { selectables[i].classList.toggle('selected', s(i)); }; }
The library can also be configured to track changes, in which case
the argument to the callback would be a Map
2 of changed
elements. This mechanism is explained in Section
3.2.
2.4 Selection geometry
The OrderedGeometry
class3 defines the selection geometry for our example.
The _elements
member is a reference to the collection of the selectable elements.
var OrderedGeometry = function (elements) { this._elements = elements; } OrderedGeometry.prototype = Object.create(multiselect.DefaultGeometry.prototype);
OrderedGeometry
inherits from DefaultGeometry
to get the default
implementations of the selection geometry methods. The superclass’
constructor is not called since the base class has no
state. OrderedGeometry
defines two methods: m2v
and
selectionDomain
.
The selection space coordinates are the indices of the selectable
elements, integers between 0
and this._elements.length - 1
.
The m2v
function finds the element on which the
mouse coordinate mp
falls on and returns the element’s index.
OrderedGeometry.prototype.m2v = function(mp) { for (var i=0; i<this._elements.length; ++i) { if (pointInRectangle(mp, this._elements[i].getBoundingClientRect())) return i; } }
The helper function pointInRectangle
checks whether a point is
inside a rectangle.
function pointInRectangle(mp, r) { return mp.x >= r.left && mp.x <= r.right && mp.y >= r.top && mp.y <= r.bottom; }
The selectionDomain
function is simple—it constructs a new Map
object with makeEmptyMap
, extracts the anchor and active end
from the selection path, and sets all indices between them to true
.
The selectionDomain
function may be called with an empty path,
which is handled by checking whether path.length === 0
.
OrderedGeometry.prototype.selectionDomain = function(path) { var J = multiselect.makeEmptyMap(); if (path.length === 0) return J; var a = multiselect.anchor(path); var b = multiselect.activeEnd(path); for (var i=Math.min(a, b); i<=Math.max(a, b); ++i) J.set(i, true); return J; }
2.5 Selection state object
The SelectionState
class maintains all the state of the selection,
including the current selection mapping, selection path, and undo and
redo stacks. It defines methods for the various selection commands
(click
, cmdClick
, shiftClick
, etc.). The SelectionState
constructor’s parameters are a selection geometry, the refresh callback, a
boolean that turns change tracking on or off, and the maximum number
of undo states. The last two can be omitted if their defaults
(false
and 10
, respectively) are suitable.
var geometry = new OrderedGeometry(selectables); var selection = new multiselect.SelectionState(geometry, refresh, false, 10);
2.6 Setting up mouse events
The event handler for the mouse down event recognizes clicks,
command-clicks, and shift-clicks, and invokes the corresponding
library functions. Detecting modifier keys is somewhat messy.
MultiselectJS provides a function modifierKeys(evt)
that translates
the event data to constants that indicate shift, command/ctrl, and
option/alt modifiers. These constants are
NONE
, SHIFT
, CMD
, SHIFT_CMD
, OPT
, and SHIFT_OPT
.
The client can certainly define its own functions to distinguish
between different mouse events if the one provided is not
adequate.
In this simple example, it suffices to define and register a handler for the mousedown event:
function mousedownHandler(evt) { evt.preventDefault(); evt.stopPropagation(); var vp = selection.geometry().m2v({ x: evt.clientX, y: evt.clientY }); switch (multiselect.modifierKeys(evt)) { case multiselect.NONE: selection.click(vp); break; case multiselect.CMD: selection.cmdClick(vp); break; case multiselect.SHIFT: selection.shiftClick(vp); break; } }; selectableArea.addEventListener('mousedown', mousedownHandler, false);
We draw attention to the simplicity of invoking MultiselectJS’s services: the
selection geometry’s m2v
function transforms the mouse position into
a selection space coordinate, which is then passed to either the click
,
cmdClick
, or shiftClick
method.
2.7 Accessing selected elements
The first example is complete except for the handler for the ``Show selected animals’’ button
that displays a list of the selected elements.
The selection.selected()
call returns the indices of the selected elements as a Set.
function showAnimals() { var s = ""; selection.selected().forEach(function(v) { s = s + selectables[v].textContent + " "; }); document.getElementById("animal_list").textContent = s; } document.getElementById("show_animals").addEventListener("click", showAnimals);
Another means to inspect the current selection state, not used here,
is the isSelected(i)
method that returns true
if the element i
is
selected and false
otherwise.
3 Example: selection geometry that is both row-wise ordered and rectangular
This section introduces a selection context that has a rather complex selection geometry. The elements are ordered row-wise, similarly to how characters of text are ordered in an editor. The anchor and the active end can be interpreted either as the end points of a range of elements in this order, or as the corners of a rectangular area. The user can use both of these mechanisms interchangeably. This kind of a dual selection mechanism is offered, for example, in Apple’s Photos application.
This section also shows how to support rubber band selection and selecting using the keyboard, how to support the undo and redo operations, and how to visualize the anchor, the active end, and the rubber band. Again, the example and its complete source code can be viewed in separate windows.
To become familiar with the supported selection features, try clicking, command-clicking, and shift clicking the elements, as well as dragging the mouse to initiate a rubber band selection. Try starting a rubber band selection both on an element and between elements, and notice how the former initiates a row-wise selection and the latter a rectangular selection. Try starting a rubber band deselection with a command-click on a selected element. Try releasing the mouse in a rubber band selection, and then picking it up again with shift-click to continue with the rubber band. To experiment with selecting with the keyboard, try the space and arrow keys with and without the shift and command modifiers. Finally, use the undo and redo operations that are, respectively, bound to option-Z and shift-option-Z keys.
3.1 Selectable elements
The selectable area is a div
. The tabIndex
attribute is defined so that
the element can acquire the keyboard focus.

In this example, the selectable elements are generated by JavaScript code:
var selectableArea2 = document.getElementById("selectable_area2"); for (var i = 0; i<400; ++i) { var e = document.createElement("span"); e.setAttribute("class", "selectable2"); e.textContent = i; selectableArea2.appendChild(e); } var selectables2 = selectableArea2.getElementsByClassName("selectable2");
We again use CSS and classes to visualize the selection state.
The selectable2
class indicates a selectable element and the selected2
class
an element that is currently selected. The style definitions are as follows:

3.2 Refreshing
As discussed above, every method of the SelectionState
class that
may change the selection state invokes the refresh callback. This
example uses tracking of changes so that the refresh callback
function only needs to iterate over the changed elements, instead of
all selectable elements. We use a refresh function that toggles a class
in further examples as well, and thus write a factory function
that can generate a refresh-callback for any set of DOM elements.
function mkRefresh (elements, cls) { return (function (changed) { changed.forEach(function (value, i) { $(elements[i]).toggleClass(cls, value); }); }); }
The refresh function for this current example is:
var refresh2 = mkRefresh(selectables2, 'selected2');
With tracking of changes on, the argument to the refresh callback is a
Map
object. Its keys are the indices of the elements that were
changed and its values are either true
or false
, each indicating whether
an element is selected or not.
3.3 Selection geometry
The selection geometry again stores a reference to the collection of
the selectable elements. It also stores a reference to a DOM object
surrounding the selectable elements. We use mouse coordinates that are
relative to this parent
object’s location on the page.
var RowwiseGeometry = function (parent, elements) { this._parent = parent; this._elements = elements; } RowwiseGeometry.prototype = Object.create(multiselect.DefaultGeometry.prototype);
Coordinates in the selection space can indicate either an element
index or a point ``in-between’’. We choose to represent a coordinate
as an object that has two members, index
and point
. The in-between
coordinate values are recognized by index
that has the value null
.
The m2v
method thus maps a mouse location mp
, which is relative to parent
,
to a coordinate object whose point
member is mp
and whose index
member is either null
or
the index of one of the elements.
RowwiseGeometry.prototype.m2v = function(mp) { for (var i=0; i<this._elements.length; ++i) { var r = getOffsetRectangle(this._parent, this._elements[i]); if (pointInRectangle(mp, r)) return { index: i, point: mp }; } return { index: null, point: mp }; }
The utility function getOffsetRectangle(a, b)
function computes the bounding box of b
in
coordinates relative to the top-left corner of the bounding box of a
:
function topLeftCorner(r) { return { x: r.left, y: r.top }; } function offsetRectangle(p, r) { return { left: r.left - p.x, top: r.top - p.y, right: r.right - p.x, bottom: r.bottom - p.y }; } function getOffsetRectangle(parent, elem) { return offsetRectangle(topLeftCorner(parent.getBoundingClientRect()), elem.getBoundingClientRect()); }
As mentioned above, the user can select either a range or a rectangular area
of elements. Which mechanism is used depends on from where a selection
command starts: if the anchor is on an element, a range is selected;
if the anchor is in-between elements, a rectangular area is selected.
The selectionDomain
function thus first inspects the anchor’s index
to determine the
kind of coordinate the anchor is, and then interprets the
anchor and the active end either as the endpoints of a range or as the
corners of a rectangle. Again, the case that path
is
empty must be handled.
RowwiseGeometry.prototype.selectionDomain = function(path) { var J = multiselect.makeEmptyMap(); if (path.length === 0) return J; var a = multiselect.anchor(path); var b = multiselect.activeEnd(path); if (a.index !== null) { // path defines a range for (var i=Math.min(a.index, b.index); i<=Math.max(a.index, b.index); ++i) J.set(i, true); } else { // path defines a rectangle var r1 = { left: Math.min(a.point.x, b.point.x), right: Math.max(a.point.x, b.point.x), top: Math.min(a.point.y, b.point.y), bottom: Math.max(a.point.y, b.point.y) }; for (var i = 0; i < this._elements.length; ++i) { var r2 = getOffsetRectangle(this._parent, this._elements[i]); if (rectangleIntersect(r1, r2)) J.set(i, true); } } return J; }
The rectangeIntersect
helper function is as follows:
function rectangleIntersect(r1, r2) { return r1.left <= r2.right && r1.right >= r2.left && r1.top <= r2.bottom && r1.bottom >= r2.top; }
This selection geometry also overrides the extendPath(path, p)
method. The
click
, cmdClick
, and shiftClick
methods call extendPath
to add
a selection space point to the current selection path. Prior to
pushing the new point to the path
array, this extendPath
implementation performs two tasks. First, only the first and last
point of the selection path (anchor and active end) are of importance
in this geometry. Therefore, if the path already
has two elements, the previous active end is discarded.4
Second, if the anchor is on an element, we insist
that the active end is also on an element: trying to extend the path
with an in-between point has no effect in this case.
RowwiseGeometry.prototype.extendPath = function(path, p) { if (path.length > 0 && multiselect.anchor(path).index !== null && p.index === null) return null; if (path.length == 2) path.pop(); path.push(p); }
By returning null
when the selection path is not changed, the extendPath
function informs the library that the selection domain does not have to be
recalculated.
3.4 Selection state object
The SelectionState
object is created as in the first example. This time, however,
we set tracking to true
since we defined the refresh2
callback to expect
a map of the changed elements as its parameter.
var geometry2 = new RowwiseGeometry(selectableArea2, selectables2); var selection2 = new multiselect.SelectionState(geometry2, refresh2, true);
3.5 Mouse events
Setting up mouse events for the current example is a bit more involved because
supporting rubber band selection requires handlers also for the mousemove
and
mouseup
events. Furthermore, in addition to the selection status of the
elements, this example visualizes the anchor, active end, and the rubber band, which
adds a few function calls to the handlers.
The interplay between the handlers of different mouse events can be designed in
many ways—the code below should be considered as one possible
arrangement. The handler for the mousedown
event in the selectable
area (parent
) is registered at all times. After a command that it recognizes, this handler
registers the handlers for the mousemove
and mouseup
events. These are recognized
within the entire document, as the mouse can wander outside of the
selectable area. The handler for the mouseup
event de-registers itself
and the mousemove
handler.
function setupMouseEvents (parent, canvas, selection) { function mousedownHandler(evt) { var mousePos = selection.geometry().m2v(offsetMousePos(parent, evt)); switch (multiselect.modifierKeys(evt)) { case multiselect.NONE: selection.click(mousePos); break; case multiselect.CMD: selection.cmdClick(mousePos); break; case multiselect.SHIFT: selection.shiftClick(mousePos); break; default: return; } selection.geometry().drawIndicators(selection, canvas, true, true, false); document.addEventListener('mousemove', mousemoveHandler, false); document.addEventListener('mouseup', mouseupHandler, false); evt.preventDefault(); evt.stopPropagation(); }; function mousemoveHandler (evt) { evt.preventDefault(); evt.stopPropagation(); var mousePos = selection.geometry().m2v(offsetMousePos(parent, evt)); selection.shiftClick(mousePos); selection.geometry().drawIndicators(selection, canvas, true, true, true); }; function mouseupHandler (evt) { document.removeEventListener('mousemove', mousemoveHandler, false); document.removeEventListener('mouseup', mouseupHandler, false); selection.geometry().drawIndicators(selection, canvas, true, true, false); }; parent.addEventListener('mousedown', mousedownHandler, false); }
There are three further noteworthy issues in the code above.
- A mouse move during rubber band selection is semantically
equivalent to a shift-click. The
mousemoveHandler
thus acquires a selection space coordinate and passes it to theshiftClick
method. - The calls to
drawIndicators
function are what display the anchor, the active end, and the rubber band indicators. These markers are drawn on a HTML5 canvas element that overlaps the selectable area. The three boolean arguments specify which of the three indicators (in the order anchor, active end, rubber band) should be shown;true
means to show,false
to hide. In this example we makedrawIndicators
a method of the geometry object. This is because we reuse thesetupMouseEvents
function in a later example that uses a different selection geometry. A different geometry usually means a different visualization, so it is convenient that the geometry object brings along this visualization function. - Even though the mouse events are a bit more complex than in the first example, MultiselectJS’s selection services are obtained by the same simple calls to the three different click methods.
We remark that a common feature in multi-selection contexts is drag-and-drop of selected elements. The above event handlers do not recognize the start of a drag-and-drop event.
A few tasks remain. First, the mouse setup code uses the helper function
offsetMousePos
to translate an event’s mouse coordinates to
coordinates relative to another DOM element (parent
). Its implementation
is as follows:
function offsetMousePos(parent, evt) { var p = topLeftCorner(parent.getClientRects()[0]); return { x: evt.clientX - p.x, y: evt.clientY - p.y }; }
Second, the event handlers must be activated:
var canvas2 = createCanvas(selectableArea2); setupMouseEvents(selectableArea2, canvas2, selection2);
Section 3.7 shows the implementations of the
createCanvas
and drawIndicators
functions.
3.6 Keyboard events
Various keyboard commands can accomplish the same selection tasks as
clicks—the selection space point associated with a keyboard command
is the value of the keyboard cursor. The keyboard cursor is often
the same as the active end; a click, command-click, and shift-click
set the cursor to and the active end to the clicked point. The cursor
can, however, deviate from the active end. For example, the arrow
keys, unmodified, move the keyboard cursor but do not change the
selection path. Further, the keyboard cursor can be defined even if
the selection path is empty, e.g., after an undo
command.
Selection geometry’s step(dir, point)
method determines how arrow
keys move the keyboard cursor. The dir
parameter is one of constants
UP
, DOWN
, LEFT
, RIGHT
. In this example, step
only moves the
cursor if it is on an element. When moving to a new element, the
point
member of the cursor object is set to the center point of the
moved-to element.
RowwiseGeometry.prototype.step = function (dir, p) { if (p.index === null) return p; // p is an "in-between" point, no change var ind = null; switch (dir) { case multiselect.LEFT: ind = Math.max(p.index - 1, 0); break; case multiselect.RIGHT: ind = Math.min(p.index + 1, this._elements.length-1); break; case multiselect.UP: ind = findClosestP.call(this, this._parent, this._elements, p.index, isAbove); break; case multiselect.DOWN: ind = findClosestP.call(this, this._parent, this._elements, p.index, function (a, b) { return isAbove(b, a); }); break; default: return p; } return { index: ind, point: centerPoint(getOffsetRectangle(this._parent, this._elements[ind])) }; }
Moving left and right is simple: decrement or increment the cursor’s
index. Moving up or down is more complex. There are several sensible
choices for what the next element above and the next element
below could mean, even the notions of above and below are ambiguous.
Here we consider one element to be above another if the former’s
center point is above the top edge of the latter. The next element
above of some element i
is the closest, in distance between center
points, of all elements that are above i
. The next element below is defined
analogously. The findClosestP(parent, elements, i, pred)
helper function
performs these determinations, finding the closest element to i
that satisfies pred
:
function centerPoint (r) { return { x: (r.left + r.right)/2, y: (r.top + r.bottom)/2 }; } function distance (p1, p2) { var dx = p1.x - p2.x; var dy = p1.y - p2.y; return Math.sqrt(dx * dx + dy * dy); } function isAbove(r1, r2) { return centerPoint(r2).y < r1.top; } function findClosestP(parent, elements, j, pred) { var r = getOffsetRectangle(parent, elements[j]); var candidateIndex = null; var candidateDistance = Number.MAX_VALUE; for (var i=0; i<elements.length; ++i) { var rc = getOffsetRectangle(parent, elements[i]); if (pred(r, rc) && distance(centerPoint(r), centerPoint(rc)) < candidateDistance) { candidateIndex = i; candidateDistance = distance(centerPoint(r), centerPoint(rc)); } } if (candidateIndex === null) return j; else return candidateIndex; }
With the step
function defined, setting up the keyboard events is
straightforward: the event handler for keydown
recognizes the key
combination of a command, invokes the desired SelectionState
’s
method, and calls the function that draws the indicators. To avoid
conflicts, we bind the handler to parent
, which is the selectable area.
In this way the bindings are only in effect when parent
has
the focus. A new mousedown
handler is added to give parent
the focus when
anything within it is clicked.
function setupKeyboardEvents(parent, canvas, selection) { parent.addEventListener('keydown', keydownHandler, false); parent.addEventListener('mousedown', function() { parent.focus(); }, false); function keydownHandler(evt) { var handled = false; var mk = multiselect.modifierKeys(evt); switch (evt.which) { case 37: handled = callArrow(mk, multiselect.LEFT); break; case 38: handled = callArrow(mk, multiselect.UP); break; case 39: handled = callArrow(mk, multiselect.RIGHT); break; case 40: handled = callArrow(mk, multiselect.DOWN); break; case 32: handled = callSpace(mk); break; case 90: handled = callUndoRedo(mk); break; default: return; // exit this handler for unrecognized keys } if (!handled) return; // they key+modifier combination was not recognized selection.geometry().drawIndicators(selection, canvas, true, true, false); evt.preventDefault(); evt.stopPropagation(); } function callUndoRedo (mk) { switch (mk) { case multiselect.OPT: selection.undo(); break; case multiselect.SHIFT_OPT: selection.redo(); break; default: return false; } return true; } function callArrow (mk, dir) { switch (mk) { case multiselect.NONE: selection.arrow(dir); break; case multiselect.CMD: selection.cmdArrow(dir); break; case multiselect.SHIFT: selection.shiftArrow(dir); break; default: return false; } return true; } function callSpace (mk) { switch (mk) { case multiselect.NONE: selection.space(); break; case multiselect.CMD: selection.cmdSpace(); break; case multiselect.SHIFT: selection.shiftSpace(); break; default: return false; } return true; } }
The main switch statement recognizes the arrow
keys, space, and the character z
(for undo and redo), and delegates
to different helper functions. The helper functions inspect the
modifiers and dispatch to the appropriate SelectionState
method,
or return false
if the key binding is not recognized.
A call to setupKeyboardEvents
registers the keyboard event handler:
setupKeyboardEvents(selectableArea2, canvas2, selection2);
To complete the keyboard selection functionality, we override the
defaultCursor
method so that the keyboard cursor has sensible defaults
when nothing has yet been selected: the arrow right and arrow down keys
start from the first element, the arrow left and arrow up keys from the last.
RowwiseGeometry.prototype.defaultCursor = function (dir) { var ind; switch (dir) { case multiselect.RIGHT: case multiselect.DOWN: ind = 0; break; case multiselect.LEFT: case multiselect.UP: ind = this._elements.length - 1; break; default: return undefined; } return { index: ind, point: centerPoint(getOffsetRectangle(this._parent, this._elements[ind])) }; }
3.7 Visualizing anchor, cursor, and rubber band
Sometimes it is useful to show where the anchor, active end, and keyboard cursor
reside. Many expect to see a rectangular rubber band when selecting
via dragging. In “lasso” selection, it is particularly important to
have a visual indicator of the selected area. To display such indicators
is outside of MultiselectJS. To help in the task, however, SelectionState
has the methods selectionPath()
and cursor()
that return the current selection path and cursor, respectively. The
former returns an array of points and the latter a single point. These
points are selection space coordinates and thus something akin to an
inverse of the m2v
transformation is necessary prior to their use in
visualization. The desired visualization, however, is likely not a
single point, but instead perhaps a frame over the selected
element. Since the details of the “inverse” transformation varies from
one selection context to another, its definition is left to
the client.
In this example the visual indicators are drawn on a canvas that is placed on
top of the selectable area. So that the canvas tracks the selectable
area at all times, the canvas’ size
and position are recalculated whenever the window
object is resized.
function createCanvas (parent) { var canvas = document.createElement("canvas"); canvas.style.position = 'absolute'; parent.insertBefore(canvas, parent.firstChild); $(window).resize(resizeCanvas); resizeCanvas(); return canvas; function resizeCanvas() { var rect = parent.getBoundingClientRect(); canvas.width = rect.right - rect.left; canvas.height = rect.bottom - rect.top; } }
The drawIndicators
function is defined as a method of the
geometry2
object. The function first clears all indicators, then
draws some or all of anchor, cursor, and rubber band based on the
drawAnchor
, drawCursor
, and drawRubber
flags. The tests for
undefined
points are to safeguard for the case where there is no
anchor (the selection path is empty) or no cursor.
The anchor is drawn as a circle if it is an in-between point (a point whose
index
is null
) and as a rectangle if it is on an element.
The cursor is not drawn at all for in-between points.
geometry2.drawIndicators = function (selection, canvas, drawAnchor, drawCursor, drawRubber) { var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); if (drawAnchor) { ctx.strokeStyle = 'DarkRed'; var p = multiselect.anchor(selection.selectionPath()); if (p !== undefined) { if (p.index === null) { // in-between point, draw a circle ctx.beginPath(); ctx.arc(p.point.x, p.point.y, 4, 0, Math.PI*2, true); ctx.stroke(); } else { // point on an element, draw a frame var r = getOffsetRectangle(canvas, selection.geometry()._elements[p.index]); ctx.strokeRect(r.left, r.top, r.right-r.left, r.bottom-r.top); } } } if (drawCursor) { ctx.strokeStyle = 'blue'; var p = selection.cursor(); if (p !== undefined && p.index !== null) { var r = getOffsetRectangle(canvas, selection.geometry()._elements[p.index]); ctx.strokeRect(r.left, r.top, r.right-r.left, r.bottom-r.top); } } if (drawRubber) { ctx.strokeStyle = 'green'; var p1 = multiselect.anchor(selection.selectionPath()); if (p1 !== undefined && p1.index === null) { var p2 = multiselect.activeEnd(selection.selectionPath()); ctx.strokeRect(Math.min(p1.point.x, p2.point.x), Math.min(p1.point.y, p2.point.y), Math.abs(p1.point.x-p2.point.x), Math.abs(p1.point.y-p2.point.y)); } } }
The second example is complete.
4 Example: snake selection geometry
The third example demonstrates a selection geometry where all points of the selection path are relevant. The selection domain is defined as all those elements that the selection path touches.5 Please experiment with the selection context to understand how this ``snake’’ selection works.
This example also demonstrates selecting elements based on a predicate. Instead of numbers, the selectable elements in this example are fish. When the Filter field is modified, all fish names that contain the filter as a substring become selected. Modifying the filter updates the active selection domain in a similar manner than shift-click; the commit button fixes the current active selection domain as an undoable state.
The example and its complete source code can be viewed in separate windows.
Filter:
The HTML code for the selectable area and the filter controls is as follows:

 Filter: <input type="text" id="filter3"></input><button id="commit_filter3">Commit</button>
4.1 Selectable elements
The selectable elements are again generated by JavaScript.
The code that populates the fish
array is in fish.js.
var selectableArea3 = $("#selectable_area3")[0]; for (var i = 0; i<fish.length; ++i) { $(selectableArea3).append("" + fish[i] + " "); } var selectables3 = $(".selectable2", selectableArea3);
The styles selectable2
and selected2
are reused from the previous example,
so we only style the selectable area:

The snake geometry again stores the DOM object of the
selectable area (parent
) and the collection of the
selectable elements (elements
). The third member k
is explained below.
4.2 Selection geometry
var SnakeGeometry = function (parent, elements) { this._parent = parent; this._elements = elements; this._k = 0; } SnakeGeometry.prototype = Object.create(multiselect.DefaultGeometry.prototype);
The snake geometry can use the default definitions of m2v
(identity)
and extendPath
(pushes a new point to the array that represents the selection path).
The selectionDomain
function is more complex.
It iterates over all line segments defined by two adjacent points
on the selection path, and for each line segment finds the elements that
the line segment intersects with, and adds them to the selection
domain. This is quite a bit of work, and thus the
function implements an optimization. When it is finished
computing a selection domain, it stores the last index of the selection
path in this._k
. When shiftClick
(which is called repeatedly during rubber band
selection) makes a call to selectionDomain
, the call includes the previously calculated
selection domain (J
) as the second parameter.
It then suffices to iterate the line segments on the path from the index k
onward. That J
is undefined
acts as a signal that
a new active set has been created and that any cached values
(here _k
) should be reset.
SnakeGeometry.prototype.selectionDomain = function(path, J) { if (J === undefined) { J = multiselect.makeEmptyMap(); this._k = 0; } var prev = this._k; for (var i = this._k; i < path.length; ++i) { for (var j = 0; j < this._elements.length; ++j) { if (lineRectIntersect(path[i], path[prev], getOffsetRectangle(this._parent, this._elements[j]))) J.set(j, true); } prev = i; } this._k = Math.max(0, path.length - 1); return J; }
A few helper functions are needed.
The lineRectIntersect
function determines if a line intersects with a rectangle,
mkRectangle
constructs a rectangle from two points:
function lineRectIntersect(p1, p2, r) { if (!rectangleIntersect(mkRectangle(p1, p2), r)) return false; // if bounding boxes do not overlap, cannot intersect if (pointEquals(p1, p2)) return pointInRectangle(p1, r); var p = {}; if (lineIntersect(p1, p2, { x: r.left, y: r.top }, { x: r.left, y: r.bottom }, p) === 1) return true; if (lineIntersect(p1, p2, { x: r.left, y: r.top }, { x: r.right, y: r.top }, p) === 1) return true; if (lineIntersect(p1, p2, { x: r.right, y: r.bottom }, { x: r.right, y: r.top }, p) === 1) return true; if (lineIntersect(p1, p2, { x: r.right, y: r.bottom }, { x: r.left, y: r.bottom }, p) === 1) return true; return pointInRectangle(p1, r) || pointInRectangle(p2, r); } function mkRectangle(p1, p2) { return { left: Math.min(p1.x, p2.x), top: Math.min(p1.y, p2.y), right: Math.max(p1.x, p2.x), bottom: Math.max(p1.y, p2.y) }; } function pointEquals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; }
The lineIntersect
function is more involved.
The code is a bit long, so we show it at the very end of this document
in Section 4.6.
This example allows selecting elements by specifying a predicate.
For this, we must override the filter
method.
It takes a predicate p
and returns a Map
of all indices for which
the predicate is true
.
4.3 Constructing the selection state object
Constructing the selection state object is as before.
We again create the refresh callback function with the mkRefresh
factory
function, and we set track changes to true
.
var geometry3 = new SnakeGeometry(selectableArea3, selectables3); var selection3 = new multiselect.SelectionState(geometry3, mkRefresh(selectables3, 'selected2'), true);
4.4 Visualizing anchor, cursor, and rubber band
Drawing the various indicators is done as before.
We again setup a canvas over the selectable area, and add
the drawIndicators
method to the selection geometry object.
The anchor and cursor are drawn as small circles,
the rubber band indicator as the line segments of
the selection path.
geometry3.drawIndicators = function (selection, canvas, drawAnchor, drawCursor, drawRubber) { var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); if (drawAnchor) { ctx.strokeStyle = 'DarkRed'; var p = multiselect.anchor(selection.selectionPath()); if (p !== undefined) { ctx.beginPath(); ctx.arc(p.x, p.y, 4, 0, Math.PI*2, true); ctx.stroke(); } } if (drawCursor) { ctx.strokeStyle = 'blue'; var p = selection.cursor(); if (p !== undefined) { ctx.beginPath(); ctx.arc(p.x, p.y, 4, 0, Math.PI*2, true); ctx.stroke(); } } if (drawRubber) { ctx.strokeStyle = 'green'; var path = selection.selectionPath(); if (path.length > 0) { ctx.moveTo(path[0].x, path[0].y); for (var i = 1; i < path.length; ++i) ctx.lineTo(path[i].x, path[i].y); ctx.stroke(); } } }
4.5 Setting up mouse events
This example reuses the createCanvas
, setupMouseEvents
, and setupKeyboardEvents
functions
from before for setting up the mouse and keyboard events.
var canvas3 = createCanvas(selectableArea3); setupMouseEvents(selectableArea3, canvas3, selection3); setupKeyboardEvents(selectableArea3, canvas3, selection3);
The Filter textbox also listens to keyboard events. When its
contents change, a new predicate is built and the SelectionState
object’s
filter
method is invoked. The predicate is true for some element
if the contents of the textbox is a substring of that element’s string value.
To be able to commit the result of filtering as
an undoable state, we bind the Commit button’s click to the commit
method.
var filter3 = $("#filter3")[0]; $(filter3).keyup(function () { var str = $(filter3).val(); selection3.filter(function(i){ return str !== "" && fish[i].indexOf(str)>-1; }); }); $("#commit_filter3").click(function(){ selection3.commit(); });
4.6 Line intersection code
This function is adapted from Prasad Mukesh’s C-code Intersection of Line Segments,
ACM Transaction of Graphics’ Graphics Gems II, p. 7–9, code: p. 473–476, xlines.c
.
/* PORTED FROM: * lines_intersect: AUTHOR: Mukesh Prasad * * This function computes whether two line segments, * respectively joining the input points (x1,y1) -- (x2,y2) * and the input points (x3,y3) -- (x4,y4) intersect. * If the lines intersect, the output variables x, y are * set to coordinates of the point of intersection. * * All values are in integers. The returned value is rounded * to the nearest integer point. * * If non-integral grid points are relevant, the function * can easily be transformed by substituting floating point * calculations instead of integer calculations. * * Entry * x1, y1, x2, y2 Coordinates of endpoints of one segment. * x3, y3, x4, y4 Coordinates of endpoints of other segment. * * Exit * x, y Coordinates of intersection point. * * The value returned by the function is one of: * * DONT_INTERSECT 0 * DO_INTERSECT 1 * COLLINEAR 2 * * Error conditions: * * Depending upon the possible ranges, and particularly on 16-bit * computers, care should be taken to protect from overflow. * * In the following code, 'long' values have been used for this * purpose, instead of 'int'. * */ function sameSigns(a, b) { return a >= 0 && b >= 0 || a < 0 && b < 0; } function lineIntersect( p1, /* First line segment */ p2, p3, /* Second line segment */ p4, p5 /* Output value: * point of intersection */ ) { const DONT_INTERSECT = 0; const DO_INTERSECT = 1; const COLLINEAR = 2; var a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns. */ var r1, r2, r3, r4; /* 'Sign' values */ var denom, offset, num; /* Intermediate values */ /* Compute a1, b1, c1, where line joining points 1 and 2 * is "a1 x + b1 y + c1 = 0". */ a1 = p2.y - p1.y; b1 = p1.x - p2.x; c1 = p2.x * p1.y - p1.x * p2.y; /* Compute r3 and r4. */ r3 = a1 * p3.x + b1 * p3.y + c1; r4 = a1 * p4.x + b1 * p4.y + c1; /* Check signs of r3 and r4. If both point 3 and point 4 lie on * same side of line 1, the line segments do not intersect. */ if ( r3 != 0 && r4 != 0 && sameSigns( r3, r4 )) return ( DONT_INTERSECT ); /* Compute a2, b2, c2 */ a2 = p4.y - p3.y; b2 = p3.x - p4.x; c2 = p4.x * p3.y - p3.x * p4.y; /* Compute r1 and r2 */ r1 = a2 * p1.x + b2 * p1.y + c2; r2 = a2 * p2.x + b2 * p2.y + c2; /* Check signs of r1 and r2. If both point 1 and point 2 lie * on same side of second line segment, the line segments do * not intersect. */ if ( r1 !== 0 && r2 !== 0 && sameSigns( r1, r2 )) return ( DONT_INTERSECT ); /* Line segments intersect: compute intersection point. */ denom = a1 * b2 - a2 * b1; if ( denom === 0 ) return ( COLLINEAR ); // offset = denom < 0 ? - denom / 2 : denom / 2; // /* The denom/2 is to get rounding instead of truncating. It // * is added or subtracted to the numerator, depending upon the // * sign of the numerator. // */ // The calculations for p5 are commented out; // we just need to know if lines intersect or not // num = b1 * c2 - b2 * c1; // p5.x = ( num < 0 ? num - offset : num + offset ) / denom; // num = a2 * c1 - a1 * c2; // p5.y = ( num < 0 ? num - offset : num + offset ) / denom; return DO_INTERSECT; }
Footnotes:
Command-click in Macs correspond to control-click in Windows. Other computers or operating systems might use still different modifier keys.
Map
is part of the draft ECMAScript 6 standard, see
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
It is supported by all major browsers.
By class we mean an object that emulates a class following popular JavaScript idioms.
The first example’s selection geometry is also such that
only the anchor and active end matter in computing the selection domain.
To avoid storing unused points in the path arrays,
we could have redefined extendPath
to
discard the intermediate points there too.
Perhaps “lasso” selection, where the user draws a path around the elements to be selected, is a more common freehand selection mechanism. Identifying the elements that intersect with an arbitrary polygon is, however, quite a bit more complex than identifying elements that intersect with a path. For this tutorial, we choose to implement the less complex selection mechanism.


John O'Donovan. AI/HCI Researcher.
Dept of Computer Science, University of California, Santa Barbara
Home
People
Research
Publications
Info for Students
Datasets
Contact
About…
Copyright 2017, John O'Donovan.


watchdog error code reference
note: all errors come from watchdog itself, not from the build process.


|
OpenMesh
|
This class provides low-level property management like adding/removing properties and access to properties. More...
#include <OpenMesh/Core/Mesh/BaseKernel.hh>
Public Types | |
| typedef PropertyContainer::iterator | prop_iterator |
| typedef PropertyContainer::const_iterator | const_prop_iterator |
Public Member Functions | |
| template<class T > | |
| void | copy_property (VPropHandleT< T > &_ph, VertexHandle _vh_from, VertexHandle _vh_to) |
| You should not use this function directly. More... | |
| template<class T > | |
| void | copy_property (HPropHandleT< T > _ph, HalfedgeHandle _hh_from, HalfedgeHandle _hh_to) |
| You should not use this function directly. More... | |
| template<class T > | |
| void | copy_property (EPropHandleT< T > _ph, EdgeHandle _eh_from, EdgeHandle _eh_to) |
| You should not use this function directly. More... | |
| template<class T > | |
| void | copy_property (FPropHandleT< T > _ph, FaceHandle _fh_from, FaceHandle _fh_to) |
| You should not use this function directly. More... | |
| void | copy_all_properties (VertexHandle _vh_from, VertexHandle _vh_to, bool _copyBuildIn=false) |
| Copies all properties from one mesh element to another (of the same type) More... | |
| void | copy_all_properties (HalfedgeHandle _hh_from, HalfedgeHandle _hh_to, bool _copyBuildIn=false) |
| Copies all properties from one mesh element to another (of the same type) More... | |
| void | copy_all_properties (EdgeHandle _eh_from, EdgeHandle _eh_to, bool _copyBuildIn=false) |
| Copies all properties from one mesh element to another (of the same type) More... | |
| void | copy_all_properties (FaceHandle _fh_from, FaceHandle _fh_to, bool _copyBuildIn=false) |
| Copies all properties from one mesh element to another (of the same type) More... | |
| size_t | n_vprops (void) const |
| size_t | n_eprops (void) const |
| size_t | n_hprops (void) const |
| size_t | n_fprops (void) const |
| size_t | n_mprops (void) const |
| BaseProperty * | _get_vprop (const std::string &_name) |
| BaseProperty * | _get_eprop (const std::string &_name) |
| BaseProperty * | _get_hprop (const std::string &_name) |
| BaseProperty * | _get_fprop (const std::string &_name) |
| BaseProperty * | _get_mprop (const std::string &_name) |
| const BaseProperty * | _get_vprop (const std::string &_name) const |
| const BaseProperty * | _get_eprop (const std::string &_name) const |
| const BaseProperty * | _get_hprop (const std::string &_name) const |
| const BaseProperty * | _get_fprop (const std::string &_name) const |
| const BaseProperty * | _get_mprop (const std::string &_name) const |
| BaseProperty & | _vprop (size_t _idx) |
| BaseProperty & | _eprop (size_t _idx) |
| BaseProperty & | _hprop (size_t _idx) |
| BaseProperty & | _fprop (size_t _idx) |
| BaseProperty & | _mprop (size_t _idx) |
| const BaseProperty & | _vprop (size_t _idx) const |
| const BaseProperty & | _eprop (size_t _idx) const |
| const BaseProperty & | _hprop (size_t _idx) const |
| const BaseProperty & | _fprop (size_t _idx) const |
| const BaseProperty & | _mprop (size_t _idx) const |
| size_t | _add_vprop (BaseProperty *_bp) |
| size_t | _add_eprop (BaseProperty *_bp) |
| size_t | _add_hprop (BaseProperty *_bp) |
| size_t | _add_fprop (BaseProperty *_bp) |
| size_t | _add_mprop (BaseProperty *_bp) |
| virtual size_t | n_vertices () const |
| virtual size_t | n_halfedges () const |
| virtual size_t | n_edges () const |
| virtual size_t | n_faces () const |
| void | property_stats () const |
| void | property_stats (std::ostream &_ostr) const |
| void | vprop_stats (std::string &_string) const |
| void | hprop_stats (std::string &_string) const |
| void | eprop_stats (std::string &_string) const |
| void | fprop_stats (std::string &_string) const |
| void | mprop_stats (std::string &_string) const |
| void | vprop_stats () const |
| void | hprop_stats () const |
| void | eprop_stats () const |
| void | fprop_stats () const |
| void | mprop_stats () const |
| void | vprop_stats (std::ostream &_ostr) const |
| void | hprop_stats (std::ostream &_ostr) const |
| void | eprop_stats (std::ostream &_ostr) const |
| void | fprop_stats (std::ostream &_ostr) const |
| void | mprop_stats (std::ostream &_ostr) const |
| prop_iterator | vprops_begin () |
| prop_iterator | vprops_end () |
| const_prop_iterator | vprops_begin () const |
| const_prop_iterator | vprops_end () const |
| prop_iterator | eprops_begin () |
| prop_iterator | eprops_end () |
| const_prop_iterator | eprops_begin () const |
| const_prop_iterator | eprops_end () const |
| prop_iterator | hprops_begin () |
| prop_iterator | hprops_end () |
| const_prop_iterator | hprops_begin () const |
| const_prop_iterator | hprops_end () const |
| prop_iterator | fprops_begin () |
| prop_iterator | fprops_end () |
| const_prop_iterator | fprops_begin () const |
| const_prop_iterator | fprops_end () const |
| prop_iterator | mprops_begin () |
| prop_iterator | mprops_end () |
| const_prop_iterator | mprops_begin () const |
| const_prop_iterator | mprops_end () const |
Add a property to a mesh item | |
| template<class T > | |
| void | add_property (VPropHandleT< T > &_ph, const std::string &_name="<vprop>") |
| You should not use this function directly. More... | |
| template<class T > | |
| void | add_property (HPropHandleT< T > &_ph, const std::string &_name="<hprop>") |
| You should not use this function directly. More... | |
| template<class T > | |
| void | add_property (EPropHandleT< T > &_ph, const std::string &_name="<eprop>") |
| You should not use this function directly. More... | |
| template<class T > | |
| void | add_property (FPropHandleT< T > &_ph, const std::string &_name="<fprop>") |
| You should not use this function directly. More... | |
| template<class T > | |
| void | add_property (MPropHandleT< T > &_ph, const std::string &_name="<mprop>") |
| You should not use this function directly. More... | |
Removing a property from a mesh tiem | |
| template<typename T > | |
| void | remove_property (VPropHandleT< T > &_ph) |
| You should not use this function directly. More... | |
| template<typename T > | |
| void | remove_property (HPropHandleT< T > &_ph) |
| You should not use this function directly. More... | |
| template<typename T > | |
| void | remove_property (EPropHandleT< T > &_ph) |
| You should not use this function directly. More... | |
| template<typename T > | |
| void | remove_property (FPropHandleT< T > &_ph) |
| You should not use this function directly. More... | |
| template<typename T > | |
| void | remove_property (MPropHandleT< T > &_ph) |
| You should not use this function directly. More... | |
Get property handle by name | |
| template<class T > | |
| bool | get_property_handle (VPropHandleT< T > &_ph, const std::string &_name) const |
| You should not use this function directly. More... | |
| template<class T > | |
| bool | get_property_handle (HPropHandleT< T > &_ph, const std::string &_name) const |
| You should not use this function directly. More... | |
| template<class T > | |
| bool | get_property_handle (EPropHandleT< T > &_ph, const std::string &_name) const |
| You should not use this function directly. More... | |
| template<class T > | |
| bool | get_property_handle (FPropHandleT< T > &_ph, const std::string &_name) const |
| You should not use this function directly. More... | |
| template<class T > | |
| bool | get_property_handle (MPropHandleT< T > &_ph, const std::string &_name) const |
| You should not use this function directly. More... | |
Access a property | |
| template<class T > | |
| PropertyT< T > & | property (VPropHandleT< T > _ph) |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| const PropertyT< T > & | property (VPropHandleT< T > _ph) const |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| PropertyT< T > & | property (HPropHandleT< T > _ph) |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| const PropertyT< T > & | property (HPropHandleT< T > _ph) const |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| PropertyT< T > & | property (EPropHandleT< T > _ph) |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| const PropertyT< T > & | property (EPropHandleT< T > _ph) const |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| PropertyT< T > & | property (FPropHandleT< T > _ph) |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| const PropertyT< T > & | property (FPropHandleT< T > _ph) const |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| PropertyT< T > & | mproperty (MPropHandleT< T > _ph) |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
| template<class T > | |
| const PropertyT< T > & | mproperty (MPropHandleT< T > _ph) const |
| In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary. More... | |
Access a property element using a handle to a mesh item | |
| template<class T > | |
| VPropHandleT< T >::reference | property (VPropHandleT< T > _ph, VertexHandle _vh) |
| You should not use this function directly. More... | |
| template<class T > | |
| VPropHandleT< T >::const_reference | property (VPropHandleT< T > _ph, VertexHandle _vh) const |
| You should not use this function directly. More... | |
| template<class T > | |
| HPropHandleT< T >::reference | property (HPropHandleT< T > _ph, HalfedgeHandle _hh) |
| You should not use this function directly. More... | |
| template<class T > | |
| HPropHandleT< T >::const_reference | property (HPropHandleT< T > _ph, HalfedgeHandle _hh) const |
| You should not use this function directly. More... | |
| template<class T > | |
| EPropHandleT< T >::reference | property (EPropHandleT< T > _ph, EdgeHandle _eh) |
| You should not use this function directly. More... | |
| template<class T > | |
| EPropHandleT< T >::const_reference | property (EPropHandleT< T > _ph, EdgeHandle _eh) const |
| You should not use this function directly. More... | |
| template<class T > | |
| FPropHandleT< T >::reference | property (FPropHandleT< T > _ph, FaceHandle _fh) |
| You should not use this function directly. More... | |
| template<class T > | |
| FPropHandleT< T >::const_reference | property (FPropHandleT< T > _ph, FaceHandle _fh) const |
| You should not use this function directly. More... | |
| template<class T > | |
| MPropHandleT< T >::reference | property (MPropHandleT< T > _ph) |
| You should not use this function directly. More... | |
| template<class T > | |
| MPropHandleT< T >::const_reference | property (MPropHandleT< T > _ph) const |
| You should not use this function directly. More... | |
Protected Member Functions | |
| BaseProperty & | _vprop (BaseHandle _h) |
| BaseProperty & | _eprop (BaseHandle _h) |
| BaseProperty & | _hprop (BaseHandle _h) |
| BaseProperty & | _fprop (BaseHandle _h) |
| BaseProperty & | _mprop (BaseHandle _h) |
| const BaseProperty & | _vprop (BaseHandle _h) const |
| const BaseProperty & | _eprop (BaseHandle _h) const |
| const BaseProperty & | _hprop (BaseHandle _h) const |
| const BaseProperty & | _fprop (BaseHandle _h) const |
| const BaseProperty & | _mprop (BaseHandle _h) const |
| void | vprops_reserve (size_t _n) const |
Reserves space for _n elements in all vertex property vectors. | |
| void | vprops_resize (size_t _n) const |
| Resizes all vertex property vectors to the specified size. | |
| void | vprops_resize_if_smaller (size_t _n) const |
Same as vprops_resize() but ignores vertex property vectors that have a size larger than _n . More... | |
| void | vprops_clear () |
| void | vprops_swap (unsigned int _i0, unsigned int _i1) const |
| void | hprops_reserve (size_t _n) const |
| void | hprops_resize (size_t _n) const |
| void | hprops_clear () |
| void | hprops_swap (unsigned int _i0, unsigned int _i1) const |
| void | eprops_reserve (size_t _n) const |
| void | eprops_resize (size_t _n) const |
| void | eprops_clear () |
| void | eprops_swap (unsigned int _i0, unsigned int _i1) const |
| void | fprops_reserve (size_t _n) const |
| void | fprops_resize (size_t _n) const |
| void | fprops_clear () |
| void | fprops_swap (unsigned int _i0, unsigned int _i1) const |
| void | mprops_resize (size_t _n) const |
| void | mprops_clear () |
This class provides low-level property management like adding/removing properties and access to properties.
Under most circumstances, it is advisable to use the high-level property management provided by PropertyManager, instead.
All operations provided by BaseKernel need at least a property handle (VPropHandleT, EPropHandleT, HPropHandleT, FPropHandleT, MPropHandleT). which keeps the data type of the property, too.
There are two types of properties:
The differentiation is only semantically, technically both are equally handled. Therefore the methods provided by the BaseKernel are applicable to both property types.
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper and/or one of its helper functions such as makePropertyManagerFromNew, makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Adds a property
Depending on the property handle type a vertex, (half-)edge, face or mesh property is added to the mesh. If the action fails the handle is invalid. On success the handle must be used to access the property data with property().
| _ph | A property handle defining the data type to bind to mesh. On success the handle is valid else invalid. |
| _name | Optional name of property. Following restrictions apply to the name:
|
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper and/or one of its helper functions such as makePropertyManagerFromNew, makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Adds a property
Depending on the property handle type a vertex, (half-)edge, face or mesh property is added to the mesh. If the action fails the handle is invalid. On success the handle must be used to access the property data with property().
| _ph | A property handle defining the data type to bind to mesh. On success the handle is valid else invalid. |
| _name | Optional name of property. Following restrictions apply to the name:
|
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper and/or one of its helper functions such as makePropertyManagerFromNew, makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Adds a property
Depending on the property handle type a vertex, (half-)edge, face or mesh property is added to the mesh. If the action fails the handle is invalid. On success the handle must be used to access the property data with property().
| _ph | A property handle defining the data type to bind to mesh. On success the handle is valid else invalid. |
| _name | Optional name of property. Following restrictions apply to the name:
|
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper and/or one of its helper functions such as makePropertyManagerFromNew, makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Adds a property
Depending on the property handle type a vertex, (half-)edge, face or mesh property is added to the mesh. If the action fails the handle is invalid. On success the handle must be used to access the property data with property().
| _ph | A property handle defining the data type to bind to mesh. On success the handle is valid else invalid. |
| _name | Optional name of property. Following restrictions apply to the name:
|
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper and/or one of its helper functions such as makePropertyManagerFromNew, makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Adds a property
Depending on the property handle type a vertex, (half-)edge, face or mesh property is added to the mesh. If the action fails the handle is invalid. On success the handle must be used to access the property data with property().
| _ph | A property handle defining the data type to bind to mesh. On success the handle is valid else invalid. |
| _name | Optional name of property. Following restrictions apply to the name:
|
|
inline |
Copies all properties from one mesh element to another (of the same type)
| _vh_from | A vertex handle - source |
| _vh_to | A vertex handle - target |
| _copyBuildIn | Should the internal properties (position, normal, texture coordinate,..) be copied? |
|
inline |
Copies all properties from one mesh element to another (of the same type)
| _hh_from | A halfedge handle - source |
| _hh_to | A halfedge handle - target |
| _copyBuildIn | Should the internal properties (position, normal, texture coordinate,..) be copied? |
|
inline |
Copies all properties from one mesh element to another (of the same type)
| _eh_from | An edge handle - source |
| _eh_to | An edge handle - target |
| _copyBuildIn | Should the internal properties (position, normal, texture coordinate,..) be copied? |
|
inline |
Copies all properties from one mesh element to another (of the same type)
| _fh_from | A face handle - source |
| _fh_to | A face handle - target |
| _copyBuildIn | Should the internal properties (position, normal, texture coordinate,..) be copied? |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::copy_to or PropertyManager::copy).
Copies a single property from one mesh element to another (of the same type)
| _ph | A vertex property handle |
| _vh_from | From vertex handle |
| _vh_to | To vertex handle |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::copy_to or PropertyManager::copy).
Copies a single property from one mesh element to another (of the same type)
| _ph | A halfedge property handle |
| _hh_from | From halfedge handle |
| _hh_to | To halfedge handle |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::copy_to or PropertyManager::copy).
Copies a single property from one mesh element to another (of the same type)
| _ph | An edge property handle |
| _eh_from | From edge handle |
| _eh_to | To edge handle |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::copy_to or PropertyManager::copy).
Copies a single property from one mesh element to another (of the same type)
| _ph | A face property handle |
| _fh_from | From face handle |
| _fh_to | To face handle |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::propertyExists) or one of its higher level helper functions such as makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Retrieves the handle to a named property by it's name.
| _ph | A property handle. On success the handle is valid else invalid. |
| _name | Name of wanted property. |
true
if such a named property is available, else false
.
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::propertyExists) or one of its higher level helper functions such as makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Retrieves the handle to a named property by it's name.
| _ph | A property handle. On success the handle is valid else invalid. |
| _name | Name of wanted property. |
true
if such a named property is available, else false
.
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::propertyExists) or one of its higher level helper functions such as makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Retrieves the handle to a named property by it's name.
| _ph | A property handle. On success the handle is valid else invalid. |
| _name | Name of wanted property. |
true
if such a named property is available, else false
.
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::propertyExists) or one of its higher level helper functions such as makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Retrieves the handle to a named property by it's name.
| _ph | A property handle. On success the handle is valid else invalid. |
| _name | Name of wanted property. |
true
if such a named property is available, else false
.
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper (e.g. PropertyManager::propertyExists) or one of its higher level helper functions such as makePropertyManagerFromExisting, or makePropertyManagerFromExistingOrNew.
Retrieves the handle to a named property by it's name.
| _ph | A property handle. On success the handle is valid else invalid. |
| _name | Name of wanted property. |
true
if such a named property is available, else false
.
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
In most cases you should use the convenient PropertyManager wrapper and use of this function should not be necessary.
Under some circumstances, however (i.e. making a property persistent), it might be necessary to use this function.
Access a property
This method returns a reference to property. The property handle must be valid! The result is unpredictable if the handle is invalid!
| _ph | A valid (!) property handle. |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper.
Return value of property for an item
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper to manage (and remove) properties.
Remove a property.
Removes the property represented by the handle from the apropriate mesh item.
| _ph | Property to be removed. The handle is invalid afterwords. |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper to manage (and remove) properties.
Remove a property.
Removes the property represented by the handle from the apropriate mesh item.
| _ph | Property to be removed. The handle is invalid afterwords. |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper to manage (and remove) properties.
Remove a property.
Removes the property represented by the handle from the apropriate mesh item.
| _ph | Property to be removed. The handle is invalid afterwords. |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper to manage (and remove) properties.
Remove a property.
Removes the property represented by the handle from the apropriate mesh item.
| _ph | Property to be removed. The handle is invalid afterwords. |
|
inline |
You should not use this function directly.
Instead, use the convenient PropertyManager wrapper to manage (and remove) properties.
Remove a property.
Removes the property represented by the handle from the apropriate mesh item.
| _ph | Property to be removed. The handle is invalid afterwords. |
|
inlineprotected |
Same as vprops_resize() but ignores vertex property vectors that have a size larger than _n
.
Use this method instead of vprops_resize() if you plan to frequently reduce and enlarge the property container and you don't want to waste time reallocating the property vectors every time.


If you like the color, click the thumb.
This is the demo app for my Likes server. It has all the JavaScript code for the user interface. See the repository for docs on how to deploy on your site.


The Activity instrument captures system activity during the app's run time.
Use the information captured by this instrument to inspect system activity in your app and how it correlates to other performance metrics, such as CPU and memory usage.
Each activity type is displayed with its own unique color on the timeline. Each activity sample includes an object information, which helps you better figure out which object initiated the system activity.
The Activity instrument is disabled by default as it records a large amount of data from numerous sources. Enabling it will introduce some performance penalty and recording documents can see a significant size increase.
You can filter which activities are displayed by clicking on the Options button ().
The detail pane displays all activity types.
From the navigation bar, you can display activity samples in summary or list.
Activity samples will be grouped by their types and objects.
Activity samples will be displayed in a list.
For each sample or group, the inspector pane shows information, such as activity type, object or starting and ending threads.


Group your locations together with projects. Invite users to manage all your locations within a project.
| Name | Created | Description | Action |
|---|---|---|---|
| {{ project.project_name }} | {{ project.description | truncate:50 }} | {{ project.created_at | humanTime }} |
Settings Delete |


An animated rainbow of square isobands:
The isoband()
function returns a filled region where the input function is less than some level, which by default is zero. To get the region between two functions, simply multiply them: the resulting combination of signs excludes the region less than the smaller functional value.
Complete code for this example:


~Impressum~
Website operator:
Responsible for the content of the website that can be found under the domain trundicho.de:
Angelo Romito -
Liability notice:
Despite careful control of the content, I assume no liability for the content of external links. The operators of the linked pages are solely responsible for their content.
Web design & programming:
Angelo Romito
Title protection:
With reference to Section 5 (3) of the Trademark Act, we claim title protection for:
trundicho & www.trundicho.de
in all spellings, fonts and forms of representation, word combinations and title combinations for printed matter, printed products, textile goods of all kinds, events, computers, radio and television broadcasts, film and sound works, sound carriers of all kinds, sound carrier doctorates, as well as other comparable works, electronic, digital media and networks , especially CD-ROM, CD-I, Offline and online services, audio text services and other online media.
Angelo Romito
25.12.2006


This page contains information regarding the functioning of weapons in Battlefield 4, throughout which multiple abbreviations may be used for brevity. Their individual meanings are as follows:
| ADS | Aim Down Sight |
| BTK | Bullets to Kill |
| EFTK | Estimated Frames to Kill |
| FSRM | First Shot Recoil Multiplier |
| FTK | Frames to Kill |
| HRec | Horizontal Recoil |
| SDEC | Spread Decrease |
| SIPS | Spread Increase per Shot |
| TTK | Time to Kill |
| VRec | Vertical Recoil |
| AR | Assault Rifle |
| BA | Bolt-Action Rifle |
| DMR | Designated Marksman Rifle |
| LMG | Light Machine Gun |
| PDW | Personal Defense Weapon |
Thanks to NoctyrneSAGA for writing this section.
Spread Decrease Per Second (SDEC) is applied whenever the weapon is in a ready to fire state. After firing a shot, a weapon has a set amount of time before it can be fired again. This time is dictated by the weapon’s Rate Of Fire. A simple example using Battlefield 3's M16A3 shows that its 800 RPM requires a 0.075 second gap between each shot. Therefore, 0.075s must pass from the previous shot fired and for no further fire inputs to be detected before SDEC activates. From there SDEC will reduce the weapon’s spread until it reaches the minimum spread value or until the weapon is fired again.
A more advanced breakdown can be performed by using frames. The 30Hz simulation rate of Battlefield 3 means the M16A3’s 800 RPM will have 2.25 frames on average between each shot. This will mean the weapon’s frame distribution is 2-2-2-3. Therefore, SDEC will only activate on the third or fourth frame depending on when no further fire input is detected.
For every shot fired, the camera is rotated a certain amount in the following frame.
Vertical Recoil (VRec) rotates the camera around the x-axis in degrees. A positive value moves the camera upwards while a negative value would move the camera downwards. As with Battlefield 3,, VRec values receive a random multiplier between 0.9 and 1.0 when recoil is generated, meaning the value stated for a given weapon may not always be exactly what is received by the player.
The First Shot Recoil Multiplier (FSRM) is applied to the weapon’s VRec if the shot fired is determined to be from a new fire input. If the fire button continues to be held down afterward, only the standard VRec without FSRM will be applied. This will continue until the fire input is released or if the weapon reloads. Once either occurs, the next new fire input will apply FSRM again. Weapons set to burst mode will apply FSRM to the final shot in the burst instead of the first shot.
Horizontal Recoil (HRec) rotates the camera around the y-axis in degrees. It can be thought of as a number line with the two HRec values specifying the maximum bounds. Each time a shot is fired, the game will randomly pick a value between these two bounds with a uniform distribution to apply to the camera.
Surrounding each soldier’s head is an invisible sphere. Projectiles passing through this sphere will deal Suppression to the soldier. The amount of Suppression imparted depends on how close to the head the projectile was and how far the soldier is from the shooter. Direct headshots yield the most Suppression.
When enough Suppression has been built up, the player will begin to experience Suppression penalties on their weapon. This threshold differs between games and is the “HighThreshold” in the game data. Once this threshold is cleared, the penalties will begin scaling up linearly until maximum Suppression is reached at which point the Suppression penalties are also at their maximum. Suppression penalties are applied before the effects of attachments take effect.
In Battlefield 4, the Suppression sphere radius is 1.5m and the HighThreshold is 0.1 or 10%. While the player’s Suppression is under 10%, they will not suffer from any Suppression penalties. Once they exceed 10%, the Suppression penalties begin to apply and scale up. At 55% Suppression, the player is experiencing half the amount of Suppression penalties specified in the game data. If the weapon has a +0.05 minSpread penalty in the data, then at this point the penalty is actually only +0.025. Only when Suppression has reached 100% will the full penalty of +0.05 be applied.
Suppression penalties in BF4 are generally:
| ARs, PDWs, LMGs, and Carbines | +0.5 degrees maxSpread +100% SIPS +100% SDEC +33% VRec +25% FSRM |
| Bolt-Action Rifles | +0.33 degrees minSpread |
| DMRs | +0.5 degrees maxSpread +50% SIPS +50% SDEC +50% VRec |
| Shotguns | +0.5 degrees maxSpread +0.115 degrees SIPS +50% VRec |
| Sidearms | +0.5 degrees maxSpread +50% SIPS +50% SDEC +50% VRec |
For more information regarding the weapon mechanics of Battlefield 4, there are a variety of forum posts created by community members that may be insightful. Below is a listing of the more notable threads:


{{name}},
Thank you for signing up for Example!
Your username is "{{username}}".
We hope you enjoy the service and be sure to let us know if there is anything that could make your experience better.


CanvasControls
A web module that adds controls to the canvas object
Usage
var cc = new CanvasControls.CanvasControls.ControllableCanvas({ //Lib.Namespace.Class
dragEnabled: true,
target: canvas //grabs first occurence if not passed
});
cc.handle(); //attach handles, can only be called once, to attach handles afterwards, use: cc._pcAdapt() or cc.mobileAdapt() after enabling them.
/**
- Voila! Try dragging the canvas!! You can add drag bounds by setting the transBounds[x, y, X, Y] array
*/
Note: In your projects you should use the heavy but minified
dist/CanvasControls.bundle.js
which is packed with polyfills of the raw but not minifieddist/babel/CanvasControls.js
Features
- Scrolling - click 'n' drag
- Zooming - mouse wheel, 2-finger pinch
- Clickable buttons/widgets


---
title: "Weather App"
date: 2020-11-15
lesson: 35
---
{% set html %}
Checking the weather near you...
{% endset %}
{% set js %}
{% endset %}
{{ html | safe }}
{% highlight "html" %} {{ html | safe }} {{ js | safe }} {% endhighlight %}
{{ js | safe }}