Skip to content

Topic/avoid separate mouse state #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 183 additions & 83 deletions Classes/PointView.sc
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@ PointView : View {
var prevColors, highlighted = false;
var connectionColor, indicesColor;
var connStrokeWidthNear = 3, connStrokeWidthFar;

var <groupColors, <colorGroups, defaultGroupColor;

// movement
@@ -42,12 +41,14 @@ PointView : View {
var <randomizedAxes; // dictionary of booleans for randomize state of each axis
var <>randomVariance; // normalized value to apply to movement speed if randomized
var extrinsicRotation = false; // flag if mouse interaction requires additional extrinsic rotation
var exRot = 0, exTum = 0, prevExRot = 0, prevExTum = 0;
var rotPxStep = 1;

// views
var <userView, <rotationView, <showView, <perspectiveView;

// other
var sdClass;


*new { |parent, bounds = (Rect(0,0, 1000, 715))|
^super.new(parent, bounds).init;
@@ -73,10 +74,12 @@ PointView : View {
randomVariance = 0.15;
connectionColor = Color.blue.alpha_(0.1);
indicesColor = Color.black;
groupColors = Color.red;
groupColors = [Color.red];
defaultGroupColor = Color.gray.alpha_(0.3);
connStrokeWidthFar = connStrokeWidthNear * pointDistScale;

sdClass = \SphericalDesign.asClass; // support for SphericalDesign without explicit dependency

userView = UserView(this, this.bounds.origin_(0@0))
.resize_(5)
.frameRate_(frameRate)
@@ -251,46 +254,29 @@ PointView : View {

triBut = Button()
.action_({
var triplets, pairsDone, pairs, connPairs, wrapped;
var triplets, connPairs, warning;

statusTxt.string_("Triangulating points...");
fork({
try {
triplets = SphericalDesign().points_(points).calcTriplets.triplets;
// reduce to point pairs to remove repeated lines
connPairs = [];
pairsDone = []; // list of pairs already found

triplets.do{ |trip|
wrapped = (trip ++ trip.first);
3.collect({ |i|
[wrapped[i], wrapped[i+1]]
}).do{ |pair|
// if (pairsDone.includes(pair).not) {
if (
pairsDone.any({ |pdone|
{ |me| me[0] == pair[0] and: { me[1] == pair[1] } }.matchItem(pdone)
}).not
) {
connPairs = connPairs.add(pair); // add pair to connection list
pairsDone = pairsDone.add(pair); // keep track of a duple that's been used
pairsDone = pairsDone.add(pair.reverse); // the reverse pair is identical
};

}
};

this.connections_(connPairs);
statusTxt.string_("");
if (\SphericalDesign.asClass.isNil) {
warning = "SphericalDesign quark is required to compute triangulation."
} {
var str = "Couldn't calulate the triangulation of points :(";
statusTxt.string_(str);
str.warn;
defer {
3.wait;
try {
triplets = SphericalDesign().points_(points).calcTriplets.triplets;
connPairs = this.reduceTriplets(triplets);
this.connections_(connPairs);
statusTxt.string_("");
} {
warning = "Couldn't calulate the triangulation of points :(";
};
};

warning !? {
statusTxt.string_(warning);
warning.warn;
defer { 3.wait; statusTxt.string_("") };
};

}, AppClock);
connChk.valueAction_(true);
})
@@ -565,12 +551,6 @@ PointView : View {
}
);

// perform additional rotation from mouse
// interaction after model rotation
if (extrinsicRotation and: { ortho.not }) {
rotated = rotated.collect{ |pnt| pnt.rotate(exRot).tumble(exTum) }
};

// orient so view matches ambisonics and return
rotated.collect{ |pnt| pnt.rotate(0.5pi).tilt(0.5pi) };
};
@@ -1129,6 +1109,11 @@ PointView : View {
};
}

// arrayOfIndices: triplets of indices of connected points
connectTriplets_ { |arrayOfIndices|
this.connections_(this.reduceTriplets(arrayOfIndices));
}

axisColors_ { |colorArray|
axisColors = colorArray;
this.changed(\axisColors, *axisColors);
@@ -1199,7 +1184,7 @@ PointView : View {
var npnts, ngrps, cnt = 0;

npnts = this.points.size;
ngrps = arrayOfColors.size;
ngrps = groupColors.size;

colorGroups = Array.fill(ngrps, { Array.new } );

@@ -1215,9 +1200,10 @@ PointView : View {
cnt = cnt + 1;
}
};
};

this.colorGroups_(colorGroups);

this.colorGroups_(colorGroups);
}
}

// Set groups of point indices which belong to each color in
@@ -1245,8 +1231,8 @@ PointView : View {

highlightPoints { |arrayOfIndices, highlightColor = (Color.red), defaultColor = (Color.gray.alpha_(0.25))|
prevColors = prPntDrawCols.copy;
this.pointColors_(highlightColor);
this.colorGroups_([arrayOfIndices], defaultColor);
this.groupColors_(highlightColor, defaultColor);
this.colorGroups_([arrayOfIndices]);
highlighted = true;
}

@@ -1301,10 +1287,15 @@ PointView : View {
var deltaX, deltaY;

extrinsicRotation = true;
deltaX = x - mouseDownPnt.x;
deltaY = y - mouseDownPnt.y;
exRot = prevExRot + (deltaX * rotPxStep);
exTum = prevExTum + (deltaY * rotPxStep);
deltaX = x - mouseMovePnt.x;
deltaY = y - mouseMovePnt.y;
this.rotate_(
wrap( this.rotate + (deltaX * rotPxStep), -1pi, 1pi)
);
this.tumble_(
wrap( this.tumble + (deltaY * rotPxStep), -1pi, 1pi )
);

mouseMovePnt = x@y;

this.refresh;
@@ -1317,8 +1308,6 @@ PointView : View {

userView.mouseUpAction_({ |v,x,y, modifiers|
mouseUpPnt = x@y;
prevExRot = exRot;
prevExTum = exTum;
});
}

@@ -1329,37 +1318,79 @@ PointView : View {
// reset rotations
reset {
extrinsicRotation = false; // flag if mouse interaction requires additional extrinsic rotation
exRot = exTum = prevExRot = prevExTum = 0;
this.rotate_(-45.degrad).tilt_(0).tumble_(0);
this.allOsc_(false).allCyc_(false);
this.refresh;
}

// reduce triplets to pairs of connections so lines aren't
// drawn multiple times. returns array of index pairs
reduceTriplets { |tripletArray|
var pairsDone, retPairs, wrapped;
// reduce to point pairs to remove repeated lines
retPairs = [];
pairsDone = []; // list of pairs already found

tripletArray.do{ |trip|
wrapped = (trip ++ trip.first);
3.collect({ |i|
[wrapped[i], wrapped[i+1]]
}).do{ |pair|
// if (pairsDone.includes(pair).not) {
if (
pairsDone.any({ |pdone|
{ |me| me[0] == pair[0] and: { me[1] == pair[1] } }.matchItem(pdone)
}).not
) {
retPairs = retPairs.add(pair); // add pair to connection list
pairsDone = pairsDone.add(pair); // keep track of a duple that's been used
pairsDone = pairsDone.add(pair.reverse); // the reverse pair is identical
};

}
};

^retPairs
}

update { |who, what ... args|
if (who.isKindOf(SphericalDesign)) {
switch (what,
\points, {
if (who.triplets == connections) {
// triplets still match connections, don't reset them
this.points_(who.points, false) // don't reset connections, assume
} {
// connections are no longer valid with the new points
this.points_(who.points, true) // reset connections
}
},
// when triplets change, it's assumed that connections are the triplets
\triplets, {
if (args[0]) {
// new triplets:
this.connections_(who.triplets)
} {
// triplets removed and not recalculated
connections = nil;
this.showConnections_(false);
}

}
)
// Support for SphericalDesign class
// It's assumed that connections are the design's
// triplets if they have been calculated (triplets.notNil).
if (sdClass.notNil) {
if (who.isKindOf(sdClass)) {
var triplets;

switch (what,
\points, {
if (who.triplets.notNil and: {
who.triplets.flat.maxItem <= who.points.size
}) {
// assume triplets are still valid, don't reset connections
// user can SphericalDesign:-resetTriplets to explicitly clear them
// it's more expensive to recalculate them with high point counts
this.points_(who.points, false)
} {
// connections are no longer valid with the new points
this.points_(who.points, true) // reset connections
}
},
// when triplets change, it's assumed that connections are the triplets
\triplets, {
if (args[0]) {
// new triplets
triplets = who.triplets;
this.connections_(this.reduceTriplets(triplets))
} {
// triplets removed and not recalculated
connections = nil;
this.showConnections_(false);
}

}
)
}
};
}

@@ -1369,12 +1400,18 @@ PointView : View {

/*
Usage
Basic usage:
(
t = TDesign(15).visualize(bounds: [200,200, 1200,700].asRect, showConnections: true)
t = TDesign(13).visualize(bounds: [200,200, 1200,700].asRect, showConnections: true)
)
t.rotate_(0.25pi)
t.tilt_(-0.25pi)
t.reset
t.calcTriplets
t.mirrorX
t.resetTriplets
t.view.highlightPoints_( (4..7), Color.yellow )
// highlight all points +Z
@@ -1383,8 +1420,71 @@ t.view.highlightPoints_((0..t.numPoints-1).select({|i| t.points[i].z > 0}))
// connect pairs
t.view.connections_((0..t.points.size-1).clump(2))
t.rotate(0.25pi)
t.tilt(-0.25pi)
t.reset
*/

/*
// directions of a 13-point t-design
var dirs = [
[ 0.0, 1.571 ], [ 0.0, 0.564 ], [ 1.571, 0.564 ],
[ 3.142, 0.564 ], [ -1.571, 0.564 ], [ 0.0, -0.046 ],
[ -1.571, -0.046 ], [ 3.142, -0.046 ], [ 1.571, -0.046 ],
[ 0.0, -0.831 ], [ -1.571, -0.831 ], [ 3.142, -0.831 ],
[ 1.571, -0.831 ]
];
~pv = PointView().directions_(dirs).front
)
(
// highlight all points above the horizon
var highPnts = ~pv.points.selectIndices{ |pnt| pnt.z > 0 };
~pv.highlightPoints(highPnts, Color.yellow);
)
// remove the highlight
~pv.removeHighlight;
// connect points with lines
~pv.connections_((0 .. ~pv.points.size-1).clump(2))
(
// ...or connect points that form a triangular mesh.
// This method is more efficient than setting
// connections to be the triplets directly, because
// some connections will be repeated
~pv.connectTriplets_([
[ 0, 1, 2 ], [ 0, 1, 4 ], [ 0, 2, 3 ],
[ 0, 3, 4 ], [ 1, 2, 8 ], [ 1, 4, 6 ],
[ 1, 5, 6 ], [ 1, 5, 8 ], [ 2, 3, 7 ],
[ 2, 7, 8 ], [ 3, 4, 6 ], [ 3, 6, 7 ],
[ 5, 6, 10 ], [ 5, 8, 12 ], [ 5, 9, 10 ],
[ 5, 9, 12 ], [ 6, 7, 11 ], [ 6, 10, 11 ],
[ 7, 8, 12 ], [ 7, 11, 12 ], [ 9, 10, 11 ],
[ 9, 11, 12 ]
])
)
// set random point colors
~pv.pointColors_(~pv.points.size.collect{ Color.rand })
// set point colors to a hue range
~pv.pointHueRange_(0.75, 0.95);
// group points by color
~pv.colorGroups_([(0..4),(5..8),(9..12)])
// need to set the group colors...
~pv.groupColors_([Color.red, Color.green, Color.yellow])
// rotate the perspective
~pv.rotate_(25.degrad)
~pv.tumble_(45.degrad)
~pv.reset
// orthographic projection down the Z axis
~pv.setOrtho('-Z')
// back to perspective view
~pv.setPerspective
~pv.showConnections = false;
*/
*/
4 changes: 2 additions & 2 deletions Classes/PointViewUI.sc
Original file line number Diff line number Diff line change
@@ -355,15 +355,15 @@ PointViewUI : View {
radianCtls = IdentityDictionary(know: true).putPairs([
\rotate, PointViewRadianCtl(
pv, "Rotate", \rotate_, \baseRotation,
[2pi, -2pi].asSpec, \radians, pv.axisColors[2]
[pi, -pi].asSpec, \radians, pv.axisColors[2]
),
\tilt, PointViewRadianCtl(
pv, "Tilt", \tilt_, \baseTilt,
[-pi, pi].asSpec, \radians, pv.axisColors[0]
),
\tumble, PointViewRadianCtl(
pv, "Tumble", \tumble_, \baseTumble,
[-pi/2, pi/2].asSpec, \radians, pv.axisColors[1]
[-pi, 1pi].asSpec, \radians, pv.axisColors[1]
)
]);
}
384 changes: 384 additions & 0 deletions HelpSource/PointView.schelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,384 @@
TITLE:: PointView
summary:: An interactive view displaying a collection of points in 3D space.
categories:: GUI
related:: Classes/Cartesian, Classes/SphericalDesign

DESCRIPTION::
An interactive view displaying a collection of points in 3D space. Points are
link::Classes/Cartesian:: points (optionally specified as directional pairs of
code::[azimuth, elevation]::) rendered with adjustable 3D perspective or orthogonal
projection. Points can be displayed with colors (link::#-colorGroups::)
or connecting lines (link::#-connections::) to illustrate relationships between
them.

The view and rotation controls are most conveniently changed through the UI
built into the view, though these methods and more are available for more elaborate
display of the points.


CLASSMETHODS::

METHOD:: new
Create a new link::Classes/PointView::.

ARGUMENT:: parent
An optional parent link::Classes/View:: in which to embed the
link::Classes/PointView::.

ARGUMENT:: bounds
An optional link::Classes/Rect:: describing the bounds of the view.


INSTANCEMETHODS::

METHOD:: points
Set the link::Classes/Array:: of link::Classes/Cartesian:: points to display.
If code::resetConnections:: is code::false::, any previously set
link::#-connections:: will remain, otherwise the default connections will be set
to sequential indices code::[[0, 1, ... numPoints-1]]::.

METHOD:: directions
Set the directions of points to display. code::dirArray:: can be an
link::Classes/Array:: of:
LIST::
##link::Classes/Array::s in the form of
code::[[azimuth, inclination], [azimuth, inclination], ...]:: in radians,
where rho is assumed to be 1. i.e. points on a unit circle.
##link::Classes/Array::s in the form of
code::[[azimuth, inclination, rho], [azimuth, inclination, rho], ...]:: in
radians.
##link::Classes/Spherical:: objects.
::
If code::resetConnections:: is code::false::, any previously set
link::#-connections:: will remain, otherwise the default connections will be set
to sequential indices code::[[0, 1, ... numPoints-1]]::. Directions are
converted to link::Classes/Cartesian:: points internally.

METHOD:: connections
Set an link::Classes/Array:: of link::Classes/Array::s, each listing the indices of
points which should be connected by lines. If
code::update:: is code::true::, the connecting lines will immediately
be shown. Connections can be toggled on and off with link::#-showConnections::.
Default connections are set to sequential indices
code::[[0, 1, ... numPoints-1]]::.

METHOD:: connectTriplets
Set an link::Classes/Array:: of link::Classes/Array::s, each listing the indices of
points in groups of three that form a triangular mesh across the points. This
method is more efficient than setting connections to be the triplets directly,
because it avoids repeated connections.


SUBSECTION::Movement: rotation

METHOD:: rotate
METHOD:: tilt
METHOD:: tumble
The rotation of the displayed points, on each axis, in radians. Default positive directions
are:
list::
##strong::rotate::: counter-clockwise
##strong::tilt::: left-up
##strong::tumble::: front-up
::

METHOD:: rotateDir
METHOD:: tiltDir
METHOD:: tumbleDir
METHOD:: allDir
A flag to specify the direction of rotation on each or all axes. A
code::dir:: of code::1:: will select the default direction for that axis (see
above) while code::-1:: will invert the direction of rotation.

METHOD:: reset
Reset rotations to defaults. (code::rotate = -45.degrad, tilt = 0, tumble = 0::)

METHOD:: rotateMode
Set the order of rotations when applying compound rotations to the points.
code::'rtt':: for Rotate-Tilt-Tumble, code::'ypr':: Yaw-Pitch-Roll.


SUBSECTION::Movement: animation

METHOD:: rotateOsc
METHOD:: tiltOsc
METHOD:: tumbleOsc
METHOD:: allOsc
A link::Classes/Boolean:: flag stating whether the display (including axes) will
oscillate around each axis or all axes.

METHOD:: rotateOscPeriod
METHOD:: tiltOscPeriod
METHOD:: tumbleOscPeriod
METHOD:: allOscPeriod
The period of oscillation on each axis or all axes, in seconds.

METHOD:: rotateOscWidth
METHOD:: tiltOscWidth
METHOD:: tumbleOscWidth
METHOD:: allOscWidth
Set the oscillation width of each axis or all axes, in radians.

METHOD:: rotateCyc
METHOD:: tiltCyc
METHOD:: tumbleCyc
METHOD:: allCyc
A link::Classes/Boolean:: flag stating whether the display (including axes) will
rotate cyclically around each axis or all axes.

METHOD:: rotateCycPeriod
METHOD:: tiltCycPeriod
METHOD:: tumbleCyclePeriod
METHOD:: allCycPeriod
The cycle period of each axis or all axes, in seconds.

METHOD:: rotateCycRate
METHOD:: tiltCycRate
METHOD:: tumbleCycRate
METHOD:: allCycRate
The cycle period of each axis or all axes, in Hertz.

METHOD:: varyMotion
Set an code::axis:: (code::'rotate'::, code::'tilt'::, or code::'tumble'::)
with a link::Classes/Boolean:: flag to randomize its motion, if it's
oscillating or cycling.

METHOD:: randomVariance
Set the random variance applied to those axes for which link::#-varyMotion::
is code::true::. The value of variance is normalized to the phase increment of
each frame, so a variance of code::0:: has no effect, while a variance of
code::1:: could potentially step twice as far (twice as fast) on each frame.
Default is code::0.15::.

METHOD:: frameRate
Set the framerate of the animation if the view is set to cycle
(link::#-rotateCyc::) or oscillate (link::#-rotateOsc::).


SUBSECTION::View options

METHOD:: pointColors
code::arrayOfColors:: can be a single link::Classes/Color::,
link::Classes/Array:: of link::Classes/Color::s. If
code::(arrayOfColors.size != numPoints)::, points will wrap through the
color array.

METHOD:: pointHueRange
Set the points to colors within a hue range. code::hueLow:: and code::hueHigh::
are code::0 <= hue < 1::. Optionally specify saturation, value, and alpha, which
are bound as in link::Classes/Color#*hsv::. If code::scramble:: is
code::true::, the colors will be randomly distributed among the points instead
of hues ascending with index.

METHOD:: groupColors
code::arrayOfColors:: can be a single link::Classes/Color::,
link::Classes/Array:: of link::Classes/Color::s. If
code::(colorGroups.size != groupColors.size)::, groups will wrap through the
color array. Indices not listed in the link::#-colorGroups:: will be assigned
the code::defaultColor::.

METHOD:: colorGroups
An link::Classes/Array:: of link::Classes/Array::s containing point indices
to be set to each of the link::#-groupColors::. If
code::(colorGroups.size != groupColors.size)::, groups will wrap through the
link::#-groupColors:: array. Indices not listed in the
link::#-colorGroups:: will be assigned the code::defaultColor::.

METHOD:: showConnections
A link::Classes/Boolean:: toggling the connection lines on and off.
link::#-connections:: must be set.

METHOD:: connectionColor
The link::Classes/Color:: of the lines showing the link::#-connections::.

METHOD:: connectionStrokeWidth
The stroke width of connection lines in pixels. The farthest connection line
widths will be scaled by link::#-pointDistScale::.

METHOD:: showAxes
A link::Classes/Boolean:: flag stating whether the display the axes at the origin.
The length of the axes can be set with link::#-axisScale::.

METHOD:: axisColors
A 3-element link::Classes/Array:: of link::Classes/Color::s in the order
code::[x,y,z]::.

METHOD:: axisScale
Adjust the length of the axes. code::scale:: of code::1:: makes the axes as long
as the distance to the outermost point. Show/hide the axes with
link::#-showAxes::.

METHOD:: showIndices
A link::Classes/Boolean:: flag indicating whether to display the point indices
or not.

METHOD:: indicesColor
Set the link::Classes/Color:: of the text of the points' indices.

METHOD:: pointSize
The diameter of the points, in pixels. If link::#-renderDistanceSize:: is
code::true::, the nearest point will be the specified size;

METHOD:: highlightPoints
Provide emphasis on specific points in the view through color.

ARGUMENT:: arrayOfIndices
An link::Classes/Array:: of indices of the points to render in
code::highlightColor::.

ARGUMENT:: highlightColor
The link::Classes/Color:: for highlighted points. Default
code::Color.red::.

ARGUMENT:: defaultColor
The link::Classes/Color:: for non-highlighted points. Default
code::Color.gray.alpha_(0.25)::.

METHOD:: removeHighlight
Restore the points' colors to the state before they were set to
link::#-highlightPoints::.

METHOD:: units
Set the units, code::'radians':: or code::'degrees'::, used by the GUI.


SUBSECTION::Perspective

METHOD:: setOrtho
Set the view to a flat orthographic projection along code::axis::. This is
useful for displaying symmetry of points through a plane.

ARGUMENT:: axis
Avaible args: code::'+X', '-X', '+Y', '-Y', '+Z', '-Z'::. The sign indicates the
direction you're looking.

ARGUMENT:: offset
Optionally offset the plane of projection by a radian amount. For example, some
spherical designs will have interesting symmetry along a code::45.degrad:: plane.

METHOD:: setPerspective
Set the view to render the points in perspective (as opposed to an orthogonal
view).

METHOD:: translateX
Translate the origin of the display on the X axis. A code::norm:: of code::-1:: and
code::1::, is far left and right of the view, respectively.

METHOD:: translateY
Translate the origin of the display on the Y axis. A code::norm:: of code::-1:: and
code::1::, is bottom and top of the view, respectively.

METHOD:: skewX
Skew the perspective along the screen's X axis. A useful range of
code::norm:: is 0 to 1.

METHOD:: skewY
Skew the perspective along the screen's Y axis. A useful range of
code::norm:: is 0 to 1.

METHOD:: originDist
Move the origin of your points closer or farther from the screen.
A useful range for code::norm:: values is about code::-0.5:: (close) to
code::3:: (far), but it depends also on the link::#-eyeDist::.

METHOD:: eyeDist
A value which makes the the depth of the perspective more or less pronounced,
qualitatively similar to moving your eye or a lens nearer or farther from your
points. Useful code::norm:: values in a range of code::1.5:: (more warped) to
code::6:: (less) are reasonable, but it depends also on the link::#-originDist::.

METHOD:: renderDistanceSize
A link::Classes/Boolean:: to determine if point size changes with distance to
enhance 3D effect.

METHOD:: pointDistScale
Scale the size of the points with distance. A code::norm:: of code::0.5:: would
scale the farthest point to be one half the size of the nearest point.

METHOD:: refresh
Refresh the view. Most methods which change render settings refresh the view
automatically.


SUBSECTION:: Internal properties

METHOD:: userView
Returns the link::Classes/UserView:: which is used for drawing the point display.

METHOD:: pointsNorm
Return the link::Classes/Array:: of link::Classes/Cartesian:: points used
internally for plotting. i.e. normalized to a radius of 1.


EXAMPLES::

code::
(
// directions of a 13-point t-design
var dirs = [
[ 0.0, 1.571 ], [ 0.0, 0.564 ], [ 1.571, 0.564 ],
[ 3.142, 0.564 ], [ -1.571, 0.564 ], [ 0.0, -0.046 ],
[ -1.571, -0.046 ], [ 3.142, -0.046 ], [ 1.571, -0.046 ],
[ 0.0, -0.831 ], [ -1.571, -0.831 ], [ 3.142, -0.831 ],
[ 1.571, -0.831 ]
];

~pv = PointView().directions_(dirs).front
)

(
// highlight all points above the horizon
var highPnts = ~pv.points.selectIndices{ |pnt| pnt.z > 0 };
~pv.highlightPoints(highPnts, Color.yellow);
)

// remove the highlight
~pv.removeHighlight;

// connect points with lines
~pv.connections_((0 .. ~pv.points.size-1).clump(2))

(
// ...or connect points that form a triangular mesh.
// This method is more efficient than setting
// connections to be the triplets directly, because
// some connections will be repeated
~pv.connectTriplets_([
[ 0, 1, 2 ], [ 0, 1, 4 ], [ 0, 2, 3 ],
[ 0, 3, 4 ], [ 1, 2, 8 ], [ 1, 4, 6 ],
[ 1, 5, 6 ], [ 1, 5, 8 ], [ 2, 3, 7 ],
[ 2, 7, 8 ], [ 3, 4, 6 ], [ 3, 6, 7 ],
[ 5, 6, 10 ], [ 5, 8, 12 ], [ 5, 9, 10 ],
[ 5, 9, 12 ], [ 6, 7, 11 ], [ 6, 10, 11 ],
[ 7, 8, 12 ], [ 7, 11, 12 ], [ 9, 10, 11 ],
[ 9, 11, 12 ]
])
)

// set random point colors
~pv.pointColors_(~pv.points.size.collect{ Color.rand })
// set point colors to a hue range
~pv.pointHueRange_(0.75, 0.95);

// group points by color
~pv.colorGroups_([(0..4),(5..8),(9..12)])
// need to set the group colors...
~pv.groupColors_([Color.red, Color.green, Color.yellow])

// rotate the perspective
~pv.rotate_(25.degrad)
~pv.tumble_(45.degrad)
~pv.reset

// orthographic projection down the Z axis
~pv.setOrtho('-Z')
// back to perspective view
~pv.setPerspective

~pv.showConnections = false;

// more options can be explored in the GUI!

::

PRIVATE:: perspectiveView, drawFunc, makePerspectiveView, baseTilt, baseRotation, baseTumble, cen, rotatePhase, rotationView, cycTilt, cycRotate, cycTumble, oscTilt, oscRotate, oscTumble, prUpdateColors, randomizedAxes, prCheckAnimate, showView, initInteractions, makeShowView, rotatePhase, tiltPhase, tumblePhase, minDim, update, updateCanvasDims, layItOut, rotateStep, init, reduceTriplets
Binary file added HelpSource/img/PointView_interface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions PointView.quark
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(
name: "PointView",
summary: "An interactive view displaying a collection of points in 3D space.",
author: "Michael McCrea",
copyright: "Ambisonic Toolkit Community, Joseph Anderson, Michael McCrea 2018",
license: "GPL",
version: "0.9.0",
schelp: "PointView",
url: "https://github.com/ambisonictoolkit/PointView",
dependencies: [
"MathLib"
],
organization: "ATK Community: http://www.ambisonictoolkit.net",
since: "2018"
)
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
The PointView Quark : Read Me
========================
_An interactive view displaying a collection of points in 3D space._

Points are `Cartesian` points (optionally specified as directional pairs of
`[azimuth, elevation]`) rendered through a 3-D or orthogonal projection. The
model of points can be rotated or animated and the perspective can also be
manipulated. Points can be displayed with colors, individually or in groups, and
connecting lines can illustrate relationships between them.

The view and rotation controls are most conveniently changed through the UI
built into the view, though these methods and more are available for more
elaborate display of the points.

![PointView Interface](HelpSource/img/PointView_interface.png)

Installing
==========

Install via SuperCollider's command line:

>`Quarks.install("https://github.com/ambisonictoolkit/PointView")`
It's recommended but not required that you also install the
[SphericalDesign]("https://github.com/ambisonictoolkit/SphericalDesign")
Quark for the ability to form a triangular mesh across convex sets of points:

>`Quarks.install("https://github.com/ambisonictoolkit/SphericalDesign")`

Feedback and Bug Reports
========================

Known issues are logged at
[GitHub](https://github.com/ambisonictoolkit/PointView/issues).


Credits
=======

The development of the Hilbert Transform Library for SuperCollider3 is supported
by
[The University of Washington's Center for Digital Arts and Experimental Media (DXARTS)](https://dxarts.washington.edu/).
&nbsp;

Copyright the ATK Community, Joseph Anderson, and Michael McCrea, 2018.

* J Anderson : [[e-mail]](mailto:joanders[at]uw.edu)
* M McCrea : [[e-mail]](mailto:mtm5[at]uw.edu)


Contributors
------------

* Michael McCrea (@mtmccrea)