from __future__ import division, print_function
from sympy.core import Expr, S, Symbol, oo, pi, sympify
from sympy.core.compatibility import as_int, range
from sympy.functions.elementary.complexes import sign
from sympy.functions.elementary.piecewise import Piecewise
from sympy.functions.elementary.trigonometric import cos, sin, tan
from sympy.geometry.exceptions import GeometryError
from sympy.logic import And
from sympy.matrices import Matrix
from sympy.simplify import simplify
from sympy.utilities import default_sort_key
from sympy.utilities.iterables import has_dups, has_variety, uniq
from .entity import GeometryEntity, GeometrySet
from .point import Point
from .ellipse import Circle
from .line import Line, Segment
from .util import _symbol
import warnings
[docs]class Polygon(GeometrySet):
"""A two-dimensional polygon.
A simple polygon in space. Can be constructed from a sequence of points
or from a center, radius, number of sides and rotation angle.
Parameters
==========
vertices : sequence of Points
Attributes
==========
area
angles
perimeter
vertices
centroid
sides
Raises
======
GeometryError
If all parameters are not Points.
If the Polygon has intersecting sides.
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment, Triangle
Notes
=====
Polygons are treated as closed paths rather than 2D areas so
some calculations can be be negative or positive (e.g., area)
based on the orientation of the points.
Any consecutive identical points are reduced to a single point
and any points collinear and between two points will be removed
unless they are needed to define an explicit intersection (see examples).
A Triangle, Segment or Point will be returned when there are 3 or
fewer points provided.
Examples
========
>>> from sympy import Point, Polygon, pi
>>> p1, p2, p3, p4, p5 = [(0, 0), (1, 0), (5, 1), (0, 1), (3, 0)]
>>> Polygon(p1, p2, p3, p4)
Polygon(Point2D(0, 0), Point2D(1, 0), Point2D(5, 1), Point2D(0, 1))
>>> Polygon(p1, p2)
Segment(Point2D(0, 0), Point2D(1, 0))
>>> Polygon(p1, p2, p5)
Segment(Point2D(0, 0), Point2D(3, 0))
While the sides of a polygon are not allowed to cross implicitly, they
can do so explicitly. For example, a polygon shaped like a Z with the top
left connecting to the bottom right of the Z must have the point in the
middle of the Z explicitly given:
>>> mid = Point(1, 1)
>>> Polygon((0, 2), (2, 2), mid, (0, 0), (2, 0), mid).area
0
>>> Polygon((0, 2), (2, 2), mid, (2, 0), (0, 0), mid).area
-2
When the the keyword `n` is used to define the number of sides of the
Polygon then a RegularPolygon is created and the other arguments are
interpreted as center, radius and rotation. The unrotated RegularPolygon
will always have a vertex at Point(r, 0) where `r` is the radius of the
circle that circumscribes the RegularPolygon. Its method `spin` can be
used to increment that angle.
>>> p = Polygon((0,0), 1, n=3)
>>> p
RegularPolygon(Point2D(0, 0), 1, 3, 0)
>>> p.vertices[0]
Point2D(1, 0)
>>> p.args[0]
Point2D(0, 0)
>>> p.spin(pi/2)
>>> p.vertices[0]
Point2D(0, 1)
"""
def __new__(cls, *args, **kwargs):
if kwargs.get('n', 0):
n = kwargs.pop('n')
args = list(args)
# return a virtual polygon with n sides
if len(args) == 2: # center, radius
args.append(n)
elif len(args) == 3: # center, radius, rotation
args.insert(2, n)
return RegularPolygon(*args, **kwargs)
vertices = [Point(a) for a in args]
# remove consecutive duplicates
nodup = []
for p in vertices:
if nodup and p == nodup[-1]:
continue
nodup.append(p)
if len(nodup) > 1 and nodup[-1] == nodup[0]:
nodup.pop() # last point was same as first
# remove collinear points unless they are shared points
got = set()
shared = set()
for p in nodup:
if p in got:
shared.add(p)
else:
got.add(p)
i = -3
while i < len(nodup) - 3 and len(nodup) > 2:
a, b, c = nodup[i], nodup[i + 1], nodup[i + 2]
if b not in shared and Point.is_collinear(a, b, c):
nodup.pop(i + 1)
if a == c:
nodup.pop(i)
else:
i += 1
vertices = list(nodup)
if len(vertices) > 3:
rv = GeometryEntity.__new__(cls, *vertices, **kwargs)
elif len(vertices) == 3:
return Triangle(*vertices, **kwargs)
elif len(vertices) == 2:
return Segment(*vertices, **kwargs)
else:
return Point(*vertices, **kwargs)
# reject polygons that have intersecting sides unless the
# intersection is a shared point or a generalized intersection.
# A self-intersecting polygon is easier to detect than a
# random set of segments since only those sides that are not
# part of the convex hull can possibly intersect with other
# sides of the polygon...but for now we use the n**2 algorithm
# and check if any side intersects with any preceding side,
# excluding the ones it is connected to
try:
convex = rv.is_convex()
except ValueError:
convex = True
if not convex:
sides = rv.sides
for i, si in enumerate(sides):
pts = si.args
# exclude the sides connected to si
for j in range(1 if i == len(sides) - 1 else 0, i - 1):
sj = sides[j]
if sj.p1 not in pts and sj.p2 not in pts:
hit = si.intersection(sj)
if not hit:
continue
hit = hit[0]
# don't complain unless the intersection is definite;
# if there are symbols present then the intersection
# might not occur; this may not be necessary since if
# the convex test passed, this will likely pass, too.
# But we are about to raise an error anyway so it
# won't matter too much.
if all(i.is_number for i in hit.args):
raise GeometryError(
"Polygon has intersecting sides.")
return rv
@property
def area(self):
"""
The area of the polygon.
Notes
=====
The area calculation can be positive or negative based on the
orientation of the points.
See Also
========
sympy.geometry.ellipse.Ellipse.area
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.area
3
"""
area = 0
args = self.args
for i in range(len(args)):
x1, y1 = args[i - 1].args
x2, y2 = args[i].args
area += x1*y2 - x2*y1
return simplify(area) / 2
@staticmethod
def _isright(a, b, c):
ba = b - a
ca = c - a
t_area = simplify(ba.x*ca.y - ca.x*ba.y)
res = t_area.is_nonpositive
if res is None:
raise ValueError("Can't determine orientation")
return res
@property
def angles(self):
"""The internal angle at each vertex.
Returns
=======
angles : dict
A dictionary where each key is a vertex and each value is the
internal angle at that vertex. The vertices are represented as
Points.
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.LinearEntity.angle_between
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.angles[p1]
pi/2
>>> poly.angles[p2]
acos(-4*sqrt(17)/17)
"""
# Determine orientation of points
args = self.vertices
cw = self._isright(args[-1], args[0], args[1])
ret = {}
for i in range(len(args)):
a, b, c = args[i - 2], args[i - 1], args[i]
ang = Line.angle_between(Line(b, a), Line(b, c))
if cw ^ self._isright(a, b, c):
ret[b] = 2*S.Pi - ang
else:
ret[b] = ang
return ret
@property
def perimeter(self):
"""The perimeter of the polygon.
Returns
=======
perimeter : number or Basic instance
See Also
========
sympy.geometry.line.Segment.length
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.perimeter
sqrt(17) + 7
"""
p = 0
args = self.vertices
for i in range(len(args)):
p += args[i - 1].distance(args[i])
return simplify(p)
@property
def vertices(self):
"""The vertices of the polygon.
Returns
=======
vertices : tuple of Points
Notes
=====
When iterating over the vertices, it is more efficient to index self
rather than to request the vertices and index them. Only use the
vertices when you want to process all of them at once. This is even
more important with RegularPolygons that calculate each vertex.
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.vertices
(Point2D(0, 0), Point2D(1, 0), Point2D(5, 1), Point2D(0, 1))
>>> poly.args[0]
Point2D(0, 0)
"""
return self.args
@property
def centroid(self):
"""The centroid of the polygon.
Returns
=======
centroid : Point
See Also
========
sympy.geometry.point.Point, sympy.geometry.util.centroid
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.centroid
Point2D(31/18, 11/18)
"""
A = 1/(6*self.area)
cx, cy = 0, 0
args = self.args
for i in range(len(args)):
x1, y1 = args[i - 1].args
x2, y2 = args[i].args
v = x1*y2 - x2*y1
cx += v*(x1 + x2)
cy += v*(y1 + y2)
return Point(simplify(A*cx), simplify(A*cy))
@property
def sides(self):
"""The line segments that form the sides of the polygon.
Returns
=======
sides : list of sides
Each side is a Segment.
Notes
=====
The Segments that represent the sides are an undirected
line segment so cannot be used to tell the orientation of
the polygon.
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.sides
[Segment(Point2D(0, 0), Point2D(1, 0)),
Segment(Point2D(1, 0), Point2D(5, 1)),
Segment(Point2D(0, 1), Point2D(5, 1)), Segment(Point2D(0, 0), Point2D(0, 1))]
"""
res = []
args = self.vertices
for i in range(-len(args), 0):
res.append(Segment(args[i], args[i + 1]))
return res
@property
def bounds(self):
"""Return a tuple (xmin, ymin, xmax, ymax) representing the bounding
rectangle for the geometric figure.
"""
verts = self.vertices
xs = [p.x for p in verts]
ys = [p.y for p in verts]
return (min(xs), min(ys), max(xs), max(ys))
[docs] def is_convex(self):
"""Is the polygon convex?
A polygon is convex if all its interior angles are less than 180
degrees.
Returns
=======
is_convex : boolean
True if this polygon is convex, False otherwise.
See Also
========
sympy.geometry.util.convex_hull
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly = Polygon(p1, p2, p3, p4)
>>> poly.is_convex()
True
"""
# Determine orientation of points
args = self.vertices
cw = self._isright(args[-2], args[-1], args[0])
for i in range(1, len(args)):
if cw ^ self._isright(args[i - 2], args[i - 1], args[i]):
return False
return True
[docs] def encloses_point(self, p):
"""
Return True if p is enclosed by (is inside of) self.
Notes
=====
Being on the border of self is considered False.
Parameters
==========
p : Point
Returns
=======
encloses_point : True, False or None
See Also
========
sympy.geometry.point.Point, sympy.geometry.ellipse.Ellipse.encloses_point
Examples
========
>>> from sympy import Polygon, Point
>>> from sympy.abc import t
>>> p = Polygon((0, 0), (4, 0), (4, 4))
>>> p.encloses_point(Point(2, 1))
True
>>> p.encloses_point(Point(2, 2))
False
>>> p.encloses_point(Point(5, 5))
False
References
==========
[1] http://paulbourke.net/geometry/polygonmesh/#insidepoly
"""
p = Point(p)
if p in self.vertices or any(p in s for s in self.sides):
return False
# move to p, checking that the result is numeric
lit = []
for v in self.vertices:
lit.append(v - p) # the difference is simplified
if lit[-1].free_symbols:
return None
poly = Polygon(*lit)
# polygon closure is assumed in the following test but Polygon removes duplicate pts so
# the last point has to be added so all sides are computed. Using Polygon.sides is
# not good since Segments are unordered.
args = poly.args
indices = list(range(-len(args), 1))
if poly.is_convex():
orientation = None
for i in indices:
a = args[i]
b = args[i + 1]
test = ((-a.y)*(b.x - a.x) - (-a.x)*(b.y - a.y)).is_negative
if orientation is None:
orientation = test
elif test is not orientation:
return False
return True
hit_odd = False
p1x, p1y = args[0].args
for i in indices[1:]:
p2x, p2y = args[i].args
if 0 > min(p1y, p2y):
if 0 <= max(p1y, p2y):
if 0 <= max(p1x, p2x):
if p1y != p2y:
xinters = (-p1y)*(p2x - p1x)/(p2y - p1y) + p1x
if p1x == p2x or 0 <= xinters:
hit_odd = not hit_odd
p1x, p1y = p2x, p2y
return hit_odd
[docs] def arbitrary_point(self, parameter='t'):
"""A parameterized point on the polygon.
The parameter, varying from 0 to 1, assigns points to the position on
the perimeter that is that fraction of the total perimeter. So the
point evaluated at t=1/2 would return the point from the first vertex
that is 1/2 way around the polygon.
Parameters
==========
parameter : str, optional
Default value is 't'.
Returns
=======
arbitrary_point : Point
Raises
======
ValueError
When `parameter` already appears in the Polygon's definition.
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy import Polygon, S, Symbol
>>> t = Symbol('t', real=True)
>>> tri = Polygon((0, 0), (1, 0), (1, 1))
>>> p = tri.arbitrary_point('t')
>>> perimeter = tri.perimeter
>>> s1, s2 = [s.length for s in tri.sides[:2]]
>>> p.subs(t, (s1 + s2/2)/perimeter)
Point2D(1, 1/2)
"""
t = _symbol(parameter)
if t.name in (f.name for f in self.free_symbols):
raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name)
sides = []
perimeter = self.perimeter
perim_fraction_start = 0
for s in self.sides:
side_perim_fraction = s.length/perimeter
perim_fraction_end = perim_fraction_start + side_perim_fraction
pt = s.arbitrary_point(parameter).subs(
t, (t - perim_fraction_start)/side_perim_fraction)
sides.append(
(pt, (And(perim_fraction_start <= t, t < perim_fraction_end))))
perim_fraction_start = perim_fraction_end
return Piecewise(*sides)
[docs] def plot_interval(self, parameter='t'):
"""The plot interval for the default geometric plot of the polygon.
Parameters
==========
parameter : str, optional
Default value is 't'.
Returns
=======
plot_interval : list (plot interval)
[parameter, lower_bound, upper_bound]
Examples
========
>>> from sympy import Polygon
>>> p = Polygon((0, 0), (1, 0), (1, 1))
>>> p.plot_interval()
[t, 0, 1]
"""
t = Symbol(parameter, real=True)
return [t, 0, 1]
[docs] def intersection(self, o):
"""The intersection of two polygons.
The intersection may be empty and can contain individual Points and
complete Line Segments.
Parameters
==========
other: Polygon
Returns
=======
intersection : list
The list of Segments and Points
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment
Examples
========
>>> from sympy import Point, Polygon
>>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)])
>>> poly1 = Polygon(p1, p2, p3, p4)
>>> p5, p6, p7 = map(Point, [(3, 2), (1, -1), (0, 2)])
>>> poly2 = Polygon(p5, p6, p7)
>>> poly1.intersection(poly2)
[Point2D(2/3, 0), Point2D(9/5, 1/5), Point2D(7/3, 1), Point2D(1/3, 1)]
"""
res = []
for side in self.sides:
inter = side.intersection(o)
if inter is not None:
res.extend(inter)
return list(uniq(res))
[docs] def distance(self, o):
"""
Returns the shortest distance between self and o.
If o is a point, then self does not need to be convex.
If o is another polygon self and o must be complex.
Examples
========
>>> from sympy import Point, Polygon, RegularPolygon
>>> p1, p2 = map(Point, [(0, 0), (7, 5)])
>>> poly = Polygon(*RegularPolygon(p1, 1, 3).vertices)
>>> poly.distance(p2)
sqrt(61)
"""
if isinstance(o, Point):
dist = oo
for side in self.sides:
current = side.distance(o)
if current == 0:
return S.Zero
elif current < dist:
dist = current
return dist
elif isinstance(o, Polygon) and self.is_convex() and o.is_convex():
return self._do_poly_distance(o)
raise NotImplementedError()
def _do_poly_distance(self, e2):
"""
Calculates the least distance between the exteriors of two
convex polygons e1 and e2. Does not check for the convexity
of the polygons as this is checked by Polygon.distance.
Notes
=====
- Prints a warning if the two polygons possibly intersect as the return
value will not be valid in such a case. For a more through test of
intersection use intersection().
See Also
========
sympy.geometry.point.Point.distance
Examples
========
>>> from sympy.geometry import Point, Polygon
>>> square = Polygon(Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0))
>>> triangle = Polygon(Point(1, 2), Point(2, 2), Point(2, 1))
>>> square._do_poly_distance(triangle)
sqrt(2)/2
Description of method used
==========================
Method:
[1] http://cgm.cs.mcgill.ca/~orm/mind2p.html
Uses rotating calipers:
[2] http://en.wikipedia.org/wiki/Rotating_calipers
and antipodal points:
[3] http://en.wikipedia.org/wiki/Antipodal_point
"""
e1 = self
'''Tests for a possible intersection between the polygons and outputs a warning'''
e1_center = e1.centroid
e2_center = e2.centroid
e1_max_radius = S.Zero
e2_max_radius = S.Zero
for vertex in e1.vertices:
r = Point.distance(e1_center, vertex)
if e1_max_radius < r:
e1_max_radius = r
for vertex in e2.vertices:
r = Point.distance(e2_center, vertex)
if e2_max_radius < r:
e2_max_radius = r
center_dist = Point.distance(e1_center, e2_center)
if center_dist <= e1_max_radius + e2_max_radius:
warnings.warn("Polygons may intersect producing erroneous output")
'''
Find the upper rightmost vertex of e1 and the lowest leftmost vertex of e2
'''
e1_ymax = Point(0, -oo)
e2_ymin = Point(0, oo)
for vertex in e1.vertices:
if vertex.y > e1_ymax.y or (vertex.y == e1_ymax.y and vertex.x > e1_ymax.x):
e1_ymax = vertex
for vertex in e2.vertices:
if vertex.y < e2_ymin.y or (vertex.y == e2_ymin.y and vertex.x < e2_ymin.x):
e2_ymin = vertex
min_dist = Point.distance(e1_ymax, e2_ymin)
'''
Produce a dictionary with vertices of e1 as the keys and, for each vertex, the points
to which the vertex is connected as its value. The same is then done for e2.
'''
e1_connections = {}
e2_connections = {}
for side in e1.sides:
if side.p1 in e1_connections:
e1_connections[side.p1].append(side.p2)
else:
e1_connections[side.p1] = [side.p2]
if side.p2 in e1_connections:
e1_connections[side.p2].append(side.p1)
else:
e1_connections[side.p2] = [side.p1]
for side in e2.sides:
if side.p1 in e2_connections:
e2_connections[side.p1].append(side.p2)
else:
e2_connections[side.p1] = [side.p2]
if side.p2 in e2_connections:
e2_connections[side.p2].append(side.p1)
else:
e2_connections[side.p2] = [side.p1]
e1_current = e1_ymax
e2_current = e2_ymin
support_line = Line(Point(S.Zero, S.Zero), Point(S.One, S.Zero))
'''
Determine which point in e1 and e2 will be selected after e2_ymin and e1_ymax,
this information combined with the above produced dictionaries determines the
path that will be taken around the polygons
'''
point1 = e1_connections[e1_ymax][0]
point2 = e1_connections[e1_ymax][1]
angle1 = support_line.angle_between(Line(e1_ymax, point1))
angle2 = support_line.angle_between(Line(e1_ymax, point2))
if angle1 < angle2:
e1_next = point1
elif angle2 < angle1:
e1_next = point2
elif Point.distance(e1_ymax, point1) > Point.distance(e1_ymax, point2):
e1_next = point2
else:
e1_next = point1
point1 = e2_connections[e2_ymin][0]
point2 = e2_connections[e2_ymin][1]
angle1 = support_line.angle_between(Line(e2_ymin, point1))
angle2 = support_line.angle_between(Line(e2_ymin, point2))
if angle1 > angle2:
e2_next = point1
elif angle2 > angle1:
e2_next = point2
elif Point.distance(e2_ymin, point1) > Point.distance(e2_ymin, point2):
e2_next = point2
else:
e2_next = point1
'''
Loop which determins the distance between anti-podal pairs and updates the
minimum distance accordingly. It repeats until it reaches the starting position.
'''
while True:
e1_angle = support_line.angle_between(Line(e1_current, e1_next))
e2_angle = pi - support_line.angle_between(Line(
e2_current, e2_next))
if (e1_angle < e2_angle) is True:
support_line = Line(e1_current, e1_next)
e1_segment = Segment(e1_current, e1_next)
min_dist_current = e1_segment.distance(e2_current)
if min_dist_current.evalf() < min_dist.evalf():
min_dist = min_dist_current
if e1_connections[e1_next][0] != e1_current:
e1_current = e1_next
e1_next = e1_connections[e1_next][0]
else:
e1_current = e1_next
e1_next = e1_connections[e1_next][1]
elif (e1_angle > e2_angle) is True:
support_line = Line(e2_next, e2_current)
e2_segment = Segment(e2_current, e2_next)
min_dist_current = e2_segment.distance(e1_current)
if min_dist_current.evalf() < min_dist.evalf():
min_dist = min_dist_current
if e2_connections[e2_next][0] != e2_current:
e2_current = e2_next
e2_next = e2_connections[e2_next][0]
else:
e2_current = e2_next
e2_next = e2_connections[e2_next][1]
else:
support_line = Line(e1_current, e1_next)
e1_segment = Segment(e1_current, e1_next)
e2_segment = Segment(e2_current, e2_next)
min1 = e1_segment.distance(e2_next)
min2 = e2_segment.distance(e1_next)
min_dist_current = min(min1, min2)
if min_dist_current.evalf() < min_dist.evalf():
min_dist = min_dist_current
if e1_connections[e1_next][0] != e1_current:
e1_current = e1_next
e1_next = e1_connections[e1_next][0]
else:
e1_current = e1_next
e1_next = e1_connections[e1_next][1]
if e2_connections[e2_next][0] != e2_current:
e2_current = e2_next
e2_next = e2_connections[e2_next][0]
else:
e2_current = e2_next
e2_next = e2_connections[e2_next][1]
if e1_current == e1_ymax and e2_current == e2_ymin:
break
return min_dist
def _svg(self, scale_factor=1., fill_color="#66cc99"):
"""Returns SVG path element for the Polygon.
Parameters
==========
scale_factor : float
Multiplication factor for the SVG stroke-width. Default is 1.
fill_color : str, optional
Hex string for fill color. Default is "#66cc99".
"""
from sympy.core.evalf import N
verts = map(N, self.vertices)
coords = ["{0},{1}".format(p.x, p.y) for p in verts]
path = "M {0} L {1} z".format(coords[0], " L ".join(coords[1:]))
return (
'<path fill-rule="evenodd" fill="{2}" stroke="#555555" '
'stroke-width="{0}" opacity="0.6" d="{1}" />'
).format(2. * scale_factor, path, fill_color)
def __eq__(self, o):
if not isinstance(o, Polygon) or len(self.args) != len(o.args):
return False
# See if self can ever be traversed (cw or ccw) from any of its
# vertices to match all points of o
args = self.args
oargs = o.args
n = len(args)
o0 = oargs[0]
for i0 in range(n):
if args[i0] == o0:
if all(args[(i0 + i) % n] == oargs[i] for i in range(1, n)):
return True
if all(args[(i0 - i) % n] == oargs[i] for i in range(1, n)):
return True
return False
def __hash__(self):
return super(Polygon, self).__hash__()
def __contains__(self, o):
"""
Return True if o is contained within the boundary lines of self.altitudes
Parameters
==========
other : GeometryEntity
Returns
=======
contained in : bool
The points (and sides, if applicable) are contained in self.
See Also
========
sympy.geometry.entity.GeometryEntity.encloses
Examples
========
>>> from sympy import Line, Segment, Point
>>> p = Point(0, 0)
>>> q = Point(1, 1)
>>> s = Segment(p, q*2)
>>> l = Line(p, q)
>>> p in q
False
>>> p in s
True
>>> q*3 in s
False
>>> s in l
True
"""
if isinstance(o, Polygon):
return self == o
elif isinstance(o, Segment):
return any(o in s for s in self.sides)
elif isinstance(o, Point):
if o in self.vertices:
return True
for side in self.sides:
if o in side:
return True
return False
[docs]class RegularPolygon(Polygon):
"""
A regular polygon.
Such a polygon has all internal angles equal and all sides the same length.
Parameters
==========
center : Point
radius : number or Basic instance
The distance from the center to a vertex
n : int
The number of sides
Attributes
==========
vertices
center
radius
rotation
apothem
interior_angle
exterior_angle
circumcircle
incircle
angles
Raises
======
GeometryError
If the `center` is not a Point, or the `radius` is not a number or Basic
instance, or the number of sides, `n`, is less than three.
Notes
=====
A RegularPolygon can be instantiated with Polygon with the kwarg n.
Regular polygons are instantiated with a center, radius, number of sides
and a rotation angle. Whereas the arguments of a Polygon are vertices, the
vertices of the RegularPolygon must be obtained with the vertices method.
See Also
========
sympy.geometry.point.Point, Polygon
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> r = RegularPolygon(Point(0, 0), 5, 3)
>>> r
RegularPolygon(Point2D(0, 0), 5, 3, 0)
>>> r.vertices[0]
Point2D(5, 0)
"""
__slots__ = ['_n', '_center', '_radius', '_rot']
def __new__(self, c, r, n, rot=0, **kwargs):
r, n, rot = map(sympify, (r, n, rot))
c = Point(c)
if not isinstance(r, Expr):
raise GeometryError("r must be an Expr object, not %s" % r)
if n.is_Number:
as_int(n) # let an error raise if necessary
if n < 3:
raise GeometryError("n must be a >= 3, not %s" % n)
obj = GeometryEntity.__new__(self, c, r, n, **kwargs)
obj._n = n
obj._center = c
obj._radius = r
obj._rot = rot
return obj
@property
def args(self):
"""
Returns the center point, the radius,
the number of sides, and the orientation angle.
Examples
========
>>> from sympy import RegularPolygon, Point
>>> r = RegularPolygon(Point(0, 0), 5, 3)
>>> r.args
(Point2D(0, 0), 5, 3, 0)
"""
return self._center, self._radius, self._n, self._rot
def __str__(self):
return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args)
def __repr__(self):
return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args)
@property
def area(self):
"""Returns the area.
Examples
========
>>> from sympy.geometry import RegularPolygon
>>> square = RegularPolygon((0, 0), 1, 4)
>>> square.area
2
>>> _ == square.length**2
True
"""
c, r, n, rot = self.args
return sign(r)*n*self.length**2/(4*tan(pi/n))
@property
def length(self):
"""Returns the length of the sides.
The half-length of the side and the apothem form two legs
of a right triangle whose hypotenuse is the radius of the
regular polygon.
Examples
========
>>> from sympy.geometry import RegularPolygon
>>> from sympy import sqrt
>>> s = square_in_unit_circle = RegularPolygon((0, 0), 1, 4)
>>> s.length
sqrt(2)
>>> sqrt((_/2)**2 + s.apothem**2) == s.radius
True
"""
return self.radius*2*sin(pi/self._n)
@property
def center(self):
"""The center of the RegularPolygon
This is also the center of the circumscribing circle.
Returns
=======
center : Point
See Also
========
sympy.geometry.point.Point, sympy.geometry.ellipse.Ellipse.center
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 5, 4)
>>> rp.center
Point2D(0, 0)
"""
return self._center
centroid = center
@property
def circumcenter(self):
"""
Alias for center.
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 5, 4)
>>> rp.circumcenter
Point2D(0, 0)
"""
return self.center
@property
def radius(self):
"""Radius of the RegularPolygon
This is also the radius of the circumscribing circle.
Returns
=======
radius : number or instance of Basic
See Also
========
sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius
Examples
========
>>> from sympy import Symbol
>>> from sympy.geometry import RegularPolygon, Point
>>> radius = Symbol('r')
>>> rp = RegularPolygon(Point(0, 0), radius, 4)
>>> rp.radius
r
"""
return self._radius
@property
def circumradius(self):
"""
Alias for radius.
Examples
========
>>> from sympy import Symbol
>>> from sympy.geometry import RegularPolygon, Point
>>> radius = Symbol('r')
>>> rp = RegularPolygon(Point(0, 0), radius, 4)
>>> rp.circumradius
r
"""
return self.radius
@property
def rotation(self):
"""CCW angle by which the RegularPolygon is rotated
Returns
=======
rotation : number or instance of Basic
Examples
========
>>> from sympy import pi
>>> from sympy.geometry import RegularPolygon, Point
>>> RegularPolygon(Point(0, 0), 3, 4, pi).rotation
pi
"""
return self._rot
@property
def apothem(self):
"""The inradius of the RegularPolygon.
The apothem/inradius is the radius of the inscribed circle.
Returns
=======
apothem : number or instance of Basic
See Also
========
sympy.geometry.line.Segment.length, sympy.geometry.ellipse.Circle.radius
Examples
========
>>> from sympy import Symbol
>>> from sympy.geometry import RegularPolygon, Point
>>> radius = Symbol('r')
>>> rp = RegularPolygon(Point(0, 0), radius, 4)
>>> rp.apothem
sqrt(2)*r/2
"""
return self.radius * cos(S.Pi/self._n)
@property
def inradius(self):
"""
Alias for apothem.
Examples
========
>>> from sympy import Symbol
>>> from sympy.geometry import RegularPolygon, Point
>>> radius = Symbol('r')
>>> rp = RegularPolygon(Point(0, 0), radius, 4)
>>> rp.inradius
sqrt(2)*r/2
"""
return self.apothem
@property
def interior_angle(self):
"""Measure of the interior angles.
Returns
=======
interior_angle : number
See Also
========
sympy.geometry.line.LinearEntity.angle_between
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 4, 8)
>>> rp.interior_angle
3*pi/4
"""
return (self._n - 2)*S.Pi/self._n
@property
def exterior_angle(self):
"""Measure of the exterior angles.
Returns
=======
exterior_angle : number
See Also
========
sympy.geometry.line.LinearEntity.angle_between
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 4, 8)
>>> rp.exterior_angle
pi/4
"""
return 2*S.Pi/self._n
@property
def circumcircle(self):
"""The circumcircle of the RegularPolygon.
Returns
=======
circumcircle : Circle
See Also
========
circumcenter, sympy.geometry.ellipse.Circle
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 4, 8)
>>> rp.circumcircle
Circle(Point2D(0, 0), 4)
"""
return Circle(self.center, self.radius)
@property
def incircle(self):
"""The incircle of the RegularPolygon.
Returns
=======
incircle : Circle
See Also
========
inradius, sympy.geometry.ellipse.Circle
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 4, 7)
>>> rp.incircle
Circle(Point2D(0, 0), 4*cos(pi/7))
"""
return Circle(self.center, self.apothem)
@property
def angles(self):
"""
Returns a dictionary with keys, the vertices of the Polygon,
and values, the interior angle at each vertex.
Examples
========
>>> from sympy import RegularPolygon, Point
>>> r = RegularPolygon(Point(0, 0), 5, 3)
>>> r.angles
{Point2D(-5/2, -5*sqrt(3)/2): pi/3,
Point2D(-5/2, 5*sqrt(3)/2): pi/3,
Point2D(5, 0): pi/3}
"""
ret = {}
ang = self.interior_angle
for v in self.vertices:
ret[v] = ang
return ret
[docs] def encloses_point(self, p):
"""
Return True if p is enclosed by (is inside of) self.
Notes
=====
Being on the border of self is considered False.
The general Polygon.encloses_point method is called only if
a point is not within or beyond the incircle or circumcircle,
respectively.
Parameters
==========
p : Point
Returns
=======
encloses_point : True, False or None
See Also
========
sympy.geometry.ellipse.Ellipse.encloses_point
Examples
========
>>> from sympy import RegularPolygon, S, Point, Symbol
>>> p = RegularPolygon((0, 0), 3, 4)
>>> p.encloses_point(Point(0, 0))
True
>>> r, R = p.inradius, p.circumradius
>>> p.encloses_point(Point((r + R)/2, 0))
True
>>> p.encloses_point(Point(R/2, R/2 + (R - r)/10))
False
>>> t = Symbol('t', real=True)
>>> p.encloses_point(p.arbitrary_point().subs(t, S.Half))
False
>>> p.encloses_point(Point(5, 5))
False
"""
c = self.center
d = Segment(c, p).length
if d >= self.radius:
return False
elif d < self.inradius:
return True
else:
# now enumerate the RegularPolygon like a general polygon.
return Polygon.encloses_point(self, p)
[docs] def spin(self, angle):
"""Increment *in place* the virtual Polygon's rotation by ccw angle.
See also: rotate method which moves the center.
>>> from sympy import Polygon, Point, pi
>>> r = Polygon(Point(0,0), 1, n=3)
>>> r.vertices[0]
Point2D(1, 0)
>>> r.spin(pi/6)
>>> r.vertices[0]
Point2D(sqrt(3)/2, 1/2)
See Also
========
rotation
rotate : Creates a copy of the RegularPolygon rotated about a Point
"""
self._rot += angle
[docs] def rotate(self, angle, pt=None):
"""Override GeometryEntity.rotate to first rotate the RegularPolygon
about its center.
>>> from sympy import Point, RegularPolygon, Polygon, pi
>>> t = RegularPolygon(Point(1, 0), 1, 3)
>>> t.vertices[0] # vertex on x-axis
Point2D(2, 0)
>>> t.rotate(pi/2).vertices[0] # vertex on y axis now
Point2D(0, 2)
See Also
========
rotation
spin : Rotates a RegularPolygon in place
"""
r = type(self)(*self.args) # need a copy or else changes are in-place
r._rot += angle
return GeometryEntity.rotate(r, angle, pt)
[docs] def scale(self, x=1, y=1, pt=None):
"""Override GeometryEntity.scale since it is the radius that must be
scaled (if x == y) or else a new Polygon must be returned.
>>> from sympy import RegularPolygon
Symmetric scaling returns a RegularPolygon:
>>> RegularPolygon((0, 0), 1, 4).scale(2, 2)
RegularPolygon(Point2D(0, 0), 2, 4, 0)
Asymmetric scaling returns a kite as a Polygon:
>>> RegularPolygon((0, 0), 1, 4).scale(2, 1)
Polygon(Point2D(2, 0), Point2D(0, 1), Point2D(-2, 0), Point2D(0, -1))
"""
if pt:
pt = Point(pt)
return self.translate(*(-pt).args).scale(x, y).translate(*pt.args)
if x != y:
return Polygon(*self.vertices).scale(x, y)
c, r, n, rot = self.args
r *= x
return self.func(c, r, n, rot)
[docs] def reflect(self, line):
"""Override GeometryEntity.reflect since this is not made of only
points.
>>> from sympy import RegularPolygon, Line
>>> RegularPolygon((0, 0), 1, 4).reflect(Line((0, 1), slope=-2))
RegularPolygon(Point2D(4/5, 2/5), -1, 4, acos(3/5))
"""
c, r, n, rot = self.args
cc = c.reflect(line)
v = self.vertices[0]
vv = v.reflect(line)
# see how much it must get spun at the new center
ang = Segment(cc, vv).angle_between(Segment(c, v))
rot = (rot + ang + pi) % (2*pi/n)
return self.func(cc, -r, n, rot)
@property
def vertices(self):
"""The vertices of the RegularPolygon.
Returns
=======
vertices : list
Each vertex is a Point.
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy.geometry import RegularPolygon, Point
>>> rp = RegularPolygon(Point(0, 0), 5, 4)
>>> rp.vertices
[Point2D(5, 0), Point2D(0, 5), Point2D(-5, 0), Point2D(0, -5)]
"""
c = self._center
r = abs(self._radius)
rot = self._rot
v = 2*S.Pi/self._n
return [Point(c.x + r*cos(k*v + rot), c.y + r*sin(k*v + rot))
for k in range(self._n)]
def __eq__(self, o):
if not isinstance(o, Polygon):
return False
elif not isinstance(o, RegularPolygon):
return Polygon.__eq__(o, self)
return self.args == o.args
def __hash__(self):
return super(RegularPolygon, self).__hash__()
[docs]class Triangle(Polygon):
"""
A polygon with three vertices and three sides.
Parameters
==========
points : sequence of Points
keyword: asa, sas, or sss to specify sides/angles of the triangle
Attributes
==========
vertices
altitudes
orthocenter
circumcenter
circumradius
circumcircle
inradius
incircle
medians
medial
nine_point_circle
Raises
======
GeometryError
If the number of vertices is not equal to three, or one of the vertices
is not a Point, or a valid keyword is not given.
See Also
========
sympy.geometry.point.Point, Polygon
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> Triangle(Point(0, 0), Point(4, 0), Point(4, 3))
Triangle(Point2D(0, 0), Point2D(4, 0), Point2D(4, 3))
Keywords sss, sas, or asa can be used to give the desired
side lengths (in order) and interior angles (in degrees) that
define the triangle:
>>> Triangle(sss=(3, 4, 5))
Triangle(Point2D(0, 0), Point2D(3, 0), Point2D(3, 4))
>>> Triangle(asa=(30, 1, 30))
Triangle(Point2D(0, 0), Point2D(1, 0), Point2D(1/2, sqrt(3)/6))
>>> Triangle(sas=(1, 45, 2))
Triangle(Point2D(0, 0), Point2D(2, 0), Point2D(sqrt(2)/2, sqrt(2)/2))
"""
def __new__(cls, *args, **kwargs):
if len(args) != 3:
if 'sss' in kwargs:
return _sss(*[simplify(a) for a in kwargs['sss']])
if 'asa' in kwargs:
return _asa(*[simplify(a) for a in kwargs['asa']])
if 'sas' in kwargs:
return _sas(*[simplify(a) for a in kwargs['sas']])
msg = "Triangle instantiates with three points or a valid keyword."
raise GeometryError(msg)
vertices = [Point(a) for a in args]
# remove consecutive duplicates
nodup = []
for p in vertices:
if nodup and p == nodup[-1]:
continue
nodup.append(p)
if len(nodup) > 1 and nodup[-1] == nodup[0]:
nodup.pop() # last point was same as first
# remove collinear points
i = -3
while i < len(nodup) - 3 and len(nodup) > 2:
a, b, c = sorted(
[nodup[i], nodup[i + 1], nodup[i + 2]], key=default_sort_key)
if Point.is_collinear(a, b, c):
nodup[i] = a
nodup[i + 1] = None
nodup.pop(i + 1)
i += 1
vertices = list(filter(lambda x: x is not None, nodup))
if len(vertices) == 3:
return GeometryEntity.__new__(cls, *vertices, **kwargs)
elif len(vertices) == 2:
return Segment(*vertices, **kwargs)
else:
return Point(*vertices, **kwargs)
@property
def vertices(self):
"""The triangle's vertices
Returns
=======
vertices : tuple
Each element in the tuple is a Point
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))
>>> t.vertices
(Point2D(0, 0), Point2D(4, 0), Point2D(4, 3))
"""
return self.args
[docs] def is_similar(t1, t2):
"""Is another triangle similar to this one.
Two triangles are similar if one can be uniformly scaled to the other.
Parameters
==========
other: Triangle
Returns
=======
is_similar : boolean
See Also
========
sympy.geometry.entity.GeometryEntity.is_similar
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))
>>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -3))
>>> t1.is_similar(t2)
True
>>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -4))
>>> t1.is_similar(t2)
False
"""
if not isinstance(t2, Polygon):
return False
s1_1, s1_2, s1_3 = [side.length for side in t1.sides]
s2 = [side.length for side in t2.sides]
def _are_similar(u1, u2, u3, v1, v2, v3):
e1 = simplify(u1/v1)
e2 = simplify(u2/v2)
e3 = simplify(u3/v3)
return bool(e1 == e2) and bool(e2 == e3)
# There's only 6 permutations, so write them out
return _are_similar(s1_1, s1_2, s1_3, *s2) or \
_are_similar(s1_1, s1_3, s1_2, *s2) or \
_are_similar(s1_2, s1_1, s1_3, *s2) or \
_are_similar(s1_2, s1_3, s1_1, *s2) or \
_are_similar(s1_3, s1_1, s1_2, *s2) or \
_are_similar(s1_3, s1_2, s1_1, *s2)
[docs] def is_equilateral(self):
"""Are all the sides the same length?
Returns
=======
is_equilateral : boolean
See Also
========
sympy.geometry.entity.GeometryEntity.is_similar, RegularPolygon
is_isosceles, is_right, is_scalene
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))
>>> t1.is_equilateral()
False
>>> from sympy import sqrt
>>> t2 = Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3)))
>>> t2.is_equilateral()
True
"""
return not has_variety(s.length for s in self.sides)
[docs] def is_isosceles(self):
"""Are two or more of the sides the same length?
Returns
=======
is_isosceles : boolean
See Also
========
is_equilateral, is_right, is_scalene
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(2, 4))
>>> t1.is_isosceles()
True
"""
return has_dups(s.length for s in self.sides)
[docs] def is_scalene(self):
"""Are all the sides of the triangle of different lengths?
Returns
=======
is_scalene : boolean
See Also
========
is_equilateral, is_isosceles, is_right
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(1, 4))
>>> t1.is_scalene()
True
"""
return not has_dups(s.length for s in self.sides)
[docs] def is_right(self):
"""Is the triangle right-angled.
Returns
=======
is_right : boolean
See Also
========
sympy.geometry.line.LinearEntity.is_perpendicular
is_equilateral, is_isosceles, is_scalene
Examples
========
>>> from sympy.geometry import Triangle, Point
>>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3))
>>> t1.is_right()
True
"""
s = self.sides
return Segment.is_perpendicular(s[0], s[1]) or \
Segment.is_perpendicular(s[1], s[2]) or \
Segment.is_perpendicular(s[0], s[2])
@property
def altitudes(self):
"""The altitudes of the triangle.
An altitude of a triangle is a segment through a vertex,
perpendicular to the opposite side, with length being the
height of the vertex measured from the line containing the side.
Returns
=======
altitudes : dict
The dictionary consists of keys which are vertices and values
which are Segments.
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment.length
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.altitudes[p1]
Segment(Point2D(0, 0), Point2D(1/2, 1/2))
"""
s = self.sides
v = self.vertices
return {v[0]: s[1].perpendicular_segment(v[0]),
v[1]: s[2].perpendicular_segment(v[1]),
v[2]: s[0].perpendicular_segment(v[2])}
@property
def orthocenter(self):
"""The orthocenter of the triangle.
The orthocenter is the intersection of the altitudes of a triangle.
It may lie inside, outside or on the triangle.
Returns
=======
orthocenter : Point
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.orthocenter
Point2D(0, 0)
"""
a = self.altitudes
v = self.vertices
return Line(a[v[0]]).intersection(Line(a[v[1]]))[0]
@property
def circumcenter(self):
"""The circumcenter of the triangle
The circumcenter is the center of the circumcircle.
Returns
=======
circumcenter : Point
See Also
========
sympy.geometry.point.Point
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.circumcenter
Point2D(1/2, 1/2)
"""
a, b, c = [x.perpendicular_bisector() for x in self.sides]
return a.intersection(b)[0]
@property
def circumradius(self):
"""The radius of the circumcircle of the triangle.
Returns
=======
circumradius : number of Basic instance
See Also
========
sympy.geometry.ellipse.Circle.radius
Examples
========
>>> from sympy import Symbol
>>> from sympy.geometry import Point, Triangle
>>> a = Symbol('a')
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, a)
>>> t = Triangle(p1, p2, p3)
>>> t.circumradius
sqrt(a**2/4 + 1/4)
"""
return Point.distance(self.circumcenter, self.vertices[0])
@property
def circumcircle(self):
"""The circle which passes through the three vertices of the triangle.
Returns
=======
circumcircle : Circle
See Also
========
sympy.geometry.ellipse.Circle
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.circumcircle
Circle(Point2D(1/2, 1/2), sqrt(2)/2)
"""
return Circle(self.circumcenter, self.circumradius)
[docs] def bisectors(self):
"""The angle bisectors of the triangle.
An angle bisector of a triangle is a straight line through a vertex
which cuts the corresponding angle in half.
Returns
=======
bisectors : dict
Each key is a vertex (Point) and each value is the corresponding
bisector (Segment).
See Also
========
sympy.geometry.point.Point, sympy.geometry.line.Segment
Examples
========
>>> from sympy.geometry import Point, Triangle, Segment
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> from sympy import sqrt
>>> t.bisectors()[p2] == Segment(Point(0, sqrt(2) - 1), Point(1, 0))
True
"""
s = self.sides
v = self.vertices
c = self.incenter
l1 = Segment(v[0], Line(v[0], c).intersection(s[1])[0])
l2 = Segment(v[1], Line(v[1], c).intersection(s[2])[0])
l3 = Segment(v[2], Line(v[2], c).intersection(s[0])[0])
return {v[0]: l1, v[1]: l2, v[2]: l3}
@property
def incenter(self):
"""The center of the incircle.
The incircle is the circle which lies inside the triangle and touches
all three sides.
Returns
=======
incenter : Point
See Also
========
incircle, sympy.geometry.point.Point
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.incenter
Point2D(-sqrt(2)/2 + 1, -sqrt(2)/2 + 1)
"""
s = self.sides
l = Matrix([s[i].length for i in [1, 2, 0]])
p = sum(l)
v = self.vertices
x = simplify(l.dot(Matrix([vi.x for vi in v]))/p)
y = simplify(l.dot(Matrix([vi.y for vi in v]))/p)
return Point(x, y)
@property
def inradius(self):
"""The radius of the incircle.
Returns
=======
inradius : number of Basic instance
See Also
========
incircle, sympy.geometry.ellipse.Circle.radius
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(4, 0), Point(0, 3)
>>> t = Triangle(p1, p2, p3)
>>> t.inradius
1
"""
return simplify(2 * self.area / self.perimeter)
@property
def incircle(self):
"""The incircle of the triangle.
The incircle is the circle which lies inside the triangle and touches
all three sides.
Returns
=======
incircle : Circle
See Also
========
sympy.geometry.ellipse.Circle
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(2, 0), Point(0, 2)
>>> t = Triangle(p1, p2, p3)
>>> t.incircle
Circle(Point2D(-sqrt(2) + 2, -sqrt(2) + 2), -sqrt(2) + 2)
"""
return Circle(self.incenter, self.inradius)
@property
def medians(self):
"""The medians of the triangle.
A median of a triangle is a straight line through a vertex and the
midpoint of the opposite side, and divides the triangle into two
equal areas.
Returns
=======
medians : dict
Each key is a vertex (Point) and each value is the median (Segment)
at that point.
See Also
========
sympy.geometry.point.Point.midpoint, sympy.geometry.line.Segment.midpoint
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.medians[p1]
Segment(Point2D(0, 0), Point2D(1/2, 1/2))
"""
s = self.sides
v = self.vertices
return {v[0]: Segment(v[0], s[1].midpoint),
v[1]: Segment(v[1], s[2].midpoint),
v[2]: Segment(v[2], s[0].midpoint)}
@property
def medial(self):
"""The medial triangle of the triangle.
The triangle which is formed from the midpoints of the three sides.
Returns
=======
medial : Triangle
See Also
========
sympy.geometry.line.Segment.midpoint
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.medial
Triangle(Point2D(1/2, 0), Point2D(1/2, 1/2), Point2D(0, 1/2))
"""
s = self.sides
return Triangle(s[0].midpoint, s[1].midpoint, s[2].midpoint)
@property
def nine_point_circle(self):
"""The nine-point circle of the triangle.
Nine-point circle is the circumcircle of the medial triangle, which
passes through the feet of altitudes and the middle points of segments
connecting the vertices and the orthocenter.
Returns
=======
nine_point_circle : Circle
See also
========
sympy.geometry.line.Segment.midpoint
sympy.geometry.polygon.Triangle.medial
sympy.geometry.polygon.Triangle.orthocenter
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.nine_point_circle
Circle(Point2D(1/4, 1/4), sqrt(2)/4)
"""
return Circle(*self.medial.vertices)
@property
def eulerline(self):
"""The Euler line of the triangle.
The line which passes through circumcenter, centroid and orthocenter.
Returns
=======
eulerline : Line (or Point for equilateral triangles in which case all
centers coincide)
Examples
========
>>> from sympy.geometry import Point, Triangle
>>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1)
>>> t = Triangle(p1, p2, p3)
>>> t.eulerline
Line(Point2D(0, 0), Point2D(1/2, 1/2))
"""
if self.is_equilateral():
return self.orthocenter
return Line(self.orthocenter, self.circumcenter)
def rad(d):
"""Return the radian value for the given degrees (pi = 180 degrees)."""
return d*pi/180
def deg(r):
"""Return the degree value for the given radians (pi = 180 degrees)."""
return r/pi*180
def _slope(d):
rv = tan(rad(d))
return rv
def _asa(d1, l, d2):
"""Return triangle having side with length l on the x-axis."""
xy = Line((0, 0), slope=_slope(d1)).intersection(
Line((l, 0), slope=_slope(180 - d2)))[0]
return Triangle((0, 0), (l, 0), xy)
def _sss(l1, l2, l3):
"""Return triangle having side of length l1 on the x-axis."""
c1 = Circle((0, 0), l3)
c2 = Circle((l1, 0), l2)
inter = [a for a in c1.intersection(c2) if a.y.is_nonnegative]
if not inter:
return None
pt = inter[0]
return Triangle((0, 0), (l1, 0), pt)
def _sas(l1, d, l2):
"""Return triangle having side with length l2 on the x-axis."""
p1 = Point(0, 0)
p2 = Point(l2, 0)
p3 = Point(cos(rad(d))*l1, sin(rad(d))*l1)
return Triangle(p1, p2, p3)


I am 21 years old and on my final year of studying Computer Science.
I enjoy making websites and making Apps on androi [Currently doing courses].
I also have a keen interest on cyber security and computer forensics
History nerd also!


As shopping was one of the most common things between everyone, the age of E-Commerce became the new phase of capitalism. Amazon and eBay created a whole new model of E-Commerce. Two important technologies and laws paired with them drove the growth of the internet. First was the silicon chip, governed by Moore's law. The law predicted that the capability of a computer will increase every couple of years. Hence, the number of the transistors on a microchip should double every 18 months. Second one was the Metcalfe’s Law, which said that everytime a new computer was added to the network, it added even more connections. As the numbers of these connections increase, the usefulness of the network increases. It also would attract more users.


Contains several utility functions for manipulating tensor-valued data (centering, multiplication from a single mode etc.) and the implementations of the following blind source separation methods for tensor-valued data: 'tPCA', 'tFOBI', 'tJADE', k-tJADE', 'tgFOBI', 'tgJADE', 'tSOBI', 'tNSS.SD', 'tNSS.JD', 'tNSS.TD.JD' and 'tPP'.
| Version: | 0.3.4 |
| Depends: | JADE |
| Imports: | Rcpp (≥ 0.12.3), tensor, tsBSS, ICtest |
| LinkingTo: | Rcpp, RcppArmadillo |
| Suggests: | stochvol, ElemStatLearn |
| Published: | 2018-03-01 |
| Author: | Joni Virta, Bing Li, Klaus Nordhausen, Hannu Oja |
| Maintainer: | Joni Virta <joni.virta at outlook.com> |
| License: | GPL-2 | GPL-3 [expanded from: GPL (≥ 2)] |
| NeedsCompilation: | yes |
| Materials: | ChangeLog |
| CRAN checks: | tensorBSS results |
| Reference manual: | tensorBSS.pdf |
| Package source: | tensorBSS_0.3.4.tar.gz |
| Windows binaries: | r-devel: tensorBSS_0.3.4.zip, r-release: tensorBSS_0.3.4.zip, r-oldrel: tensorBSS_0.3.4.zip |
| OS X binaries: | r-release: tensorBSS_0.3.4.tgz, r-oldrel: tensorBSS_0.3.4.tgz |
| Old sources: | tensorBSS archive |
Please use the canonical form https://CRAN.R-project.org/package=tensorBSS to link to this page.


EURAX- crotamiton cream
EURAX- crotamiton lotion
Ranbaxy Laboratories Inc.
Eurax (crotamiton, USP) is a scabicidal and antipruritic agent available as a cream or lotion for topical use only. Eurax provides 10% (w/w) of the synthetic, crotamiton, USP, in a vanishing-cream or emollient-lotion base containing: carbomer-934, cetyl alcohol, diazolidinylurea, dimethicone, fragrance, laureth-23, magnesium aluminum silicate, magnesium nitrate, methylchloroisothiazolinone, methylisothiazolinone, petrolatum, propylene glycol, sodium hydroxide, steareth-2, and water. In addition, the cream contains glyceryl stearate.
Crotamiton is N-ethyl-N-(o-methylphenyl)-2-butenamide and its structural formula is:
Crotamiton, USP is a colorless to slightly yellowish oil, having a faint amine-like odor. It is miscible with alcohol and with methanol. Crotamiton is a mixture of the cis and trans isomers. Its molecular weight is 203.28.
Eurax has scabicidal and antipruritic actions. The mechanisms of these actions are not known. The pharmacokinetics of crotamiton and its degree of systemic absorption following topical application have not been determined.
For eradication of scabies (Sarcoptes scabiei) and for symptomatic treatment of pruritic skin.
Eurax should not be applied topically to patients who develop a sensitivity or are allergic to it or who manifest a primary irritation response to topical medications.
If severe irritation or sensitization develops, treatment with this product should be discontinued and appropriate therapy instituted.
Eurax should not be applied in the eyes or mouth because it may cause irritation. It should not be applied to acutely inflamed skin or raw or weeping surfaces until the acute inflammation has subsided.
Long-term carcinogenicity studies in animals have not been conducted.
Animal reproduction studies have not been conducted with Eurax. It is also not known whether Eurax can cause fetal harm when applied topically to a pregnant woman or can affect reproduction capacity. Eurax should be given to a pregnant woman only if clearly needed.
Clinical studies with Eurax (crotamiton, USP) Lotion/Cream did not include sufficient numbers of subjects aged 65 years and older to determine whether they respond differently than younger subjects. Other reported clinical experience has not identified differences in responses between elderly and younger patients, but greater sensitivity of some older individuals cannot be ruled out.
Primary irritation reactions, such as dermatitis, pruritus, and rash, and allergic sensitivity reactions have been reported in a few patients.
There is no specific information on the effect of overtreatment with repeated topical applications in humans. A death was reported but cause was not confirmed.
Accidental oral ingestion may be accompanied by burning sensation in the mouth, irritation of the buccal, esophageal and gastric mucosa, nausea, vomiting, abdominal pain.
If accidental ingestion occurs, call your Poison Control Center.
In Scabies: Thoroughly massage into the skin of the whole body from the chin down, paying particular attention to all folds and creases. A second application is advisable 24 hours later. Clothing and bed linen should be changed the next morning.
In Pruritus: Massage gently into affected areas until medication is completely absorbed. Repeat as needed.
LOTION: Shake well before using.
Eurax® (crotamiton, USP) Cream, 10% is a white to yellowish-white soft cream with a perfumed characteristic odor and supplied as:
60 g tubes NDC 10631-091-60 (NSN 6505-00-116-0200)
Eurax® (crotamiton, USP) Lotion, 10% is a white to yellowish-white lotion having a characteristic perfumed odor and supplied as:
60 g (2 oz.) bottle NDC 10631-092-60 (NSN 6505-01-153-4423)
454 g (16 oz.) bottle NDC 10631-092-16
SHAKE WELL before using.
Store at room temperature.
Keep out of reach of children.
To report SUSPECTED ADVERSE REACTIONS, contact the FDA at 1-800-FDA-1088 or www.fda.gov/medwatch.
RANBAXY
Jacksonville, FL 32257 USA
129845
September 2012
|
EURAX
crotamiton cream | ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
|
|
EURAX
crotamiton lotion | ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||
|
| Labeler - Ranbaxy Laboratories Inc. (169932519) |
| Registrant - Ranbaxy Laboratories Inc. (169932519) |
| Establishment | |||
| Name | Address | ID/FEI | Business Operations |
|---|---|---|---|
| DPT Laboratories, Ltd | 832224526 | MANUFACTURE(10631-091, 10631-092), PACK(10631-091, 10631-092) |


--- layout: archive title: "Publications" permalink: /publications/ author_profile: true --- {% include base_path %}
You can also view my publications on Google Scholar, my University of Manchester research page, or my ORCID.
Web Structure Derived Clustering for Optimised Web Accessibility Evaluation.
Alexander Hambley, Yeliz Yesilada, Markel Vigo, Simon Harper.
Proceedings of the ACM Web Conference 2023. April 2023.
OPTIMAL-EM: Optimised Population Sourcing for Web Accessibility Evaluation.
Alexander Hambley, Yeliz Yesilada, Markel Vigo, Simon Harper.
The 20th International Web for All Conference. April 2023.
Towards Optimised Population Sourcing for Web Accessibility Evaluation.
Alexander Hambley.
The 20th International Web for All Conference. April 2023.
Optimising the Website Accessibility Conformance Evaluation Methodology. Best Paper Winner.
Alexander Hambley, Yeliz Yesilada, Markel Vigo, Simon Harper.
The 19th International Web for All Conference. April 2022.
Empirical Web Accessibility Evaluation for Blind Web Users.
Alexander Hambley.
ACM SIGACCESS Accessibility and Computing. January 2021.


$KMD is excited to announce the release of the Antara Framework on July 15, 2019. It includes the creation of customizable SmartChains, each with a built-in library of powerful modules and an integration layer that allows devs to build blockchain-based apps in any language...


Unilock is a Simple, Aesthetic Password Manager to serve all your password saving needs.
  Comes with complete encryption, so your passwords are safe forever!
  Auto generates safe passwords for all your accounts!
You will never have to remember a password again!
GET STARTED


-
Faster
&
More
Powerful
Slider Revolution is the highly acclaimed
slide-based displaying solution, thousands of
businesses, theme developers and everyday
people use and love!
1000's
Of Happy Users
Rekord Breaking
Sales on CodeCanyon
A Professional Solution
4.7/5 Stars
Customer Rating
-
INTRODUCING
BRAND NEW FEATURES
animate
CHARACTERS
SINGLE WORDS
LINES
Lorem ipsum dolor sit amet, consetetur
sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et
dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam.
-
Caption Selection
Custom Animation Editor
With Live Preview
With Style Preview
Big
Improvements
-
Design
&
Create
With Total Layer Control
-
Timelined Captions
Create Custom Animations
with our Brand-New
Transition Builder
-
Extensive Video Support
Youtube
Vimeo
Self-Hosted HTML 5
Next Slide: Full Screen Video
-


Widget¶
from flexx import app, ui
class Example(ui.Widget):
''' A red widget '''
CSS = ".flx-Example {background:#f00; min-width:20px; min-height:20px;}"
-
class
flexx.ui.
Widget
(*init_args, **kwargs)¶ Inherits from:
Model
Base widget class.
When subclassing a Widget to create a compound widget (a widget that acts as a container for other widgets), use the
init()
method to initialize the child widgets. This method is called while the widget is the current widget. Similarly, theinit()
method of the JS part of a subclass can be used to initialize the Widget (e.g. create the Phosphor widget and HTML DOM elements).-
base_size
¶ property – The given size of the widget when it is in a layout that allows explicit sizing, or the base-size in a BoxPanel or GridPanel.
-
children
¶ property – The child widgets of this widget. Setting this property will update the “parent” property of the old and new children.
-
container
¶ property – The id of the DOM element that contains this widget if parent is None. Use ‘body’ to make this widget the root.
-
css_class
¶ property – The extra CSS class name to asign to the DOM element. Spaces can be used to delimit multiple names. Note that the DOM element already has a css class-name corresponding to its class (e.g. ‘flx-Widget) and all its superclasses.
-
dispose
()¶ Overloaded version of dispose() that will also dispose any child widgets.
-
flex
¶ property – How much space this widget takes (relative to the other widgets) when contained in a flexible layout such as BoxLayout, BoxPanel, FormLayout or GridPanel. A flex of 0 means to take the minimum size. Flex is a two-element tuple, but both values can be specified at once by specifying a scalar.
-
icon
¶ property – The icon for this widget. This is used is some widgets classes, and is used as the app’s icon if this is the main widget.
Can be a url, a relative url to a shared asset, or a base64 encoded image. In the future this may also support names in icon packs like fontaweome.
-
init
()¶ Overload this to initialize a cusom widget. When called, this widget is the current parent.
-
key_down
¶ emitter – Event emitted when a key is pressed down while this widget has focus. A key event has the following attributes:
- key: the character corresponding to the key being pressed, or a key name like “Escape”, “Alt”, “Enter”.
- modifiers: list of strings “Alt”, “Shift”, “Ctrl”, “Meta” for modifier keys pressed down at the time of the event.
A browser may associate certain actions with certain key presses. If this browser action is unwanted, it can be disabled by overloading this emitter:
@event.emitter def key_down(self, e): # Prevent browser's default reaction to function keys ev = super().key_press(e) if ev.key.startswith('F'): e.preventDefault() return ev
-
key_press
¶ emitter – Event emitted when a key is pressed down. This event does not fire for the pressing of a modifier keys. See key_down for details.
-
key_up
¶ emitter – Event emitted when a key is released while this widget has focus. See key_down for details.
-
mouse_down
¶ emitter – Event emitted when the mouse is pressed down.
A mouse event has the following attributes:
- pos: the mouse position, in pixels, relative to this widget
- page_pos: the mouse position relative to the page
- button: what button the event is about, 1, 2, 3 are left, right, middle, respectively. 0 indicates no button.
- buttons: what buttons where pressed at the time of the event.
- modifiers: list of strings “Alt”, “Shift”, “Ctrl”, “Meta” for modifier keys pressed down at the time of the event.
-
mouse_move
¶ emitter – Event fired when the mouse is moved inside the canvas. See mouse_down for details.
-
mouse_up
¶ emitter – Event emitted when the mouse is pressed up.
See mouse_down() for a description of the event object.
-
mouse_wheel
¶ emitter – Event emitted when the mouse wheel is used.
See mouse_down() for a description of the event object. Additional event attributes:
- hscroll: amount of scrolling in horizontal direction
- vscroll: amount of scrolling in vertical direction
-
parent
¶ property – The parent widget, or None if it has no parent. Setting this property will update the “children” property of the old and new parent.
-
pos
¶ property – The position of the widget when it in a layout that allows positioning, this can be an arbitrary position (e.g. in PinBoardLayout) or the selection of column and row in a GridPanel.
-
style
¶ property – CSS style options for this widget object. e.g.
"background: #f00; color: #0f0;"
. If the given value is a dict, its key-value pairs are converted to a CSS style string. Note that setting this propery is cumulative; setting it back to the empty string does not undo previously set styling. A better approach might be to add CSS to the CSS class attribute and usecss_class
.
-
tabindex
¶ property – The index used to determine widget order when the user iterates through the widgets using tab. This also determines if a widget is able to receive key events. Flexx automatically sets this property when it should emit key events. Effect of possible values on underlying DOM element:
- None: element cannot have focus unless its a special element like
a link or form control (default).
-1: element can have focus, but is not reachable via tab.
0: element can have focus, and is reachable via tab in the order at which the element is defined.
1 and up: element can have focus, and the tab-order is determined by the value of tabindex.
-
title
¶ property – The string title of this widget. This is used to mark the widget in e.g. a tab layout or form layout, and is used as the app’s title if this is the main widget.
-


Hello there, my name is Yiting Liu. I am currently studying in the Interactive Telecommunications Program in New York University. I love cooking, making music/singing, physical computing, and creative coding.
I also have a blog of all my projects here.
I am interested in exploring collective forces of a community and relationships between people. I am learning how to tell a story effectively through my practices. Currently, I am spedning my time learning Max/MSP and Unity for my music interaction piece and game development.
If you are interested, check out my Github repo. Let's connect!


Bolivia funeral procession turns violent – in pictures
by Michael WilliamsMarchers clash with police during the funerals of eight indigenous people killed by security forces in La Paz
Locals attend the funeral procession of eight supporters of Bolivia’s ex-president Evo Morales.
Photograph: Aizar Raldes/AFP via Getty Images
Thousands of people join the procession as it winds its way through the streets.
Photograph: Getty Images
A woman holds up a photograph of one of the deceased.
Photograph: Rodrigo Sura/EPA
Relatives of Antonio Quispe lead the procession of the victims killed during clashes with police at the Senkata fuel plant.

Photograph: Ronaldo Schemidt/AFP via Getty Images
Women carry flowers during the procession in La Paz.
Photograph: Ronaldo Schemidt/AFP via Getty Images
Pallbearers with a coffin that holds the remains of a person killed the clashes at the fuel plant.
Photograph: Juan Karita/AP
A woman grieves over the coffin of a person killed during recent clashes.
Photograph: Marco Bello/Reuters
Mourners next to the coffin of one of eight supporters of Bolivia’s ex-president Evo Morales.
Photograph: Aizar Raldes/AFP via Getty Images
The cortège winds its way high above the city of La Paz.
Photograph: Ronaldo Schemidt/AFP via Getty Images
An indigenous woman pays her respects.
Photograph: Marco Bello/Reuters
Demonstrators drag an effigy of Bolivia’s interim President Jeanine Añez in La Paz.
Photograph: Natacha Pisarenko/AP
A policeman speaks to a crying girl as anti-government demonstrators clash with the police.
Photograph: Getty Images
Supporters of Evo Morales stand on an armoured vehicle, while carrying one of the coffins.
Photograph: Marco Bello/Reuters
People confront riot police during the funeral procession.
Photograph: Ronaldo Schemidt/AFP via Getty Images
Police officers spray teargas at the mourners.
Photograph: Getty Images
A woman angrily confronts riot police during the funeral procession.
Photograph: Aizar Raldes/AFP via Getty Images
A man is arrested by police on a motorcycle.
Photograph: Jorge Bernal/AFP via Getty Images
A female demonstrator is detained by riot police.
Photograph: Marco Bello/Reuters
Teargas is fired at supporters of former Bolivian president Evo Morales.
Photograph: Aizar Raldes/AFP via Getty Images
People scatter as teargas is fired into the crowd.
Photograph: Marco Bello/Reuters
Coffins of two the dead are dropped as teargas engulfs the procession.
Photograph: Ronaldo Schemidt/AFP via Getty Images
Rescue personnel assist a young girl after police fired teargas to break up the procession.
Photograph: Ronaldo Schemidt/AFP via Getty Images
A man throws a teargas canister back at riot police.
Photograph: Jorge Bernal/AFP via Getty Images
Coffins lie in the middle of a street after police launched teargas.
Photograph: Juan Karita/AP


Constellation has an automatic backup feature that periodically creates a backup of your graph while you work (regardless of whether the graph has ever been saved or not. By default, the auto backup feature is enabled and it is executed every 10 minutes.
By clicking "Setup → Options → Constellation", the user can control the activation of the feature and the frequency it is executed.
While working in Constellation, if the system were to experience a computer crash or power outage, or if for some reason the application crashes without giving you the opportunity to save your changes, then there may be a lose of data since you had not had the opportunity to save your work.
When Constellation is restarted, the application will first check to see if any of the auto backup files were associated with unsaved graphs. If there are any files, the user will be asked whether they would like to reload them. Once these files have been reloaded, the user can continue to work on the graphs.


|
f1tenth_gym
|
Public Member Functions | |
| def | __init__ (self, width, height, *args, **kwargs) |
| def | update_map (self, map_path, map_ext) |
| def | on_resize (self, width, height) |
| def | on_mouse_drag (self, x, y, dx, dy, buttons, modifiers) |
| def | on_mouse_scroll (self, x, y, dx, dy) |
| def | on_close (self) |
| def | on_draw (self) |
| def | update_obs (self, obs) |
Public Attributes | |
| left | |
| right | |
| bottom | |
| top | |
| zoom_level | |
| zoomed_width | |
| zoomed_height | |
| batch | |
| map_points | |
| poses | |
| vertices | |
| score_label | |
| fps_display | |
| ego_idx | |
| cars | |
A window class inherited from pyglet.window.Window, handles the camera/projection interaction, resizing window, and rendering the environment
| def f110_gym.envs.rendering.EnvRenderer.__init__ | ( | self, | |
| width, | |||
| height, | |||
| * | args, | ||
| ** | kwargs | ||
| ) |
Class constructor Args: width (int): width of the window height (int): height of the window Returns: None
| def f110_gym.envs.rendering.EnvRenderer.on_close | ( | self | ) |
Callback function when the 'x' is clicked on the window, overrides inherited method. Also throws exception to end the python program when in a loop. Args: None Returns: None Raises: Exception: with a message that indicates the rendering window was closed
| def f110_gym.envs.rendering.EnvRenderer.on_draw | ( | self | ) |
Function when the pyglet is drawing. The function draws the batch created that includes the map points, the agent polygons, and the information text, and the fps display. Args: None Returns: None
| def f110_gym.envs.rendering.EnvRenderer.on_mouse_drag | ( | self, | |
| x, | |||
| y, | |||
| dx, | |||
| dy, | |||
| buttons, | |||
| modifiers | |||
| ) |
Callback function on mouse drag, overrides inherited method. Args: x (int): Distance in pixels from the left edge of the window. y (int): Distance in pixels from the bottom edge of the window. dx (int): Relative X position from the previous mouse position. dy (int): Relative Y position from the previous mouse position. buttons (int): Bitwise combination of the mouse buttons currently pressed. modifiers (int): Bitwise combination of any keyboard modifiers currently active. Returns: None
| def f110_gym.envs.rendering.EnvRenderer.on_mouse_scroll | ( | self, | |
| x, | |||
| y, | |||
| dx, | |||
| dy | |||
| ) |
Callback function on mouse scroll, overrides inherited method. Args: x (int): Distance in pixels from the left edge of the window. y (int): Distance in pixels from the bottom edge of the window. scroll_x (float): Amount of movement on the horizontal axis. scroll_y (float): Amount of movement on the vertical axis. Returns: None
| def f110_gym.envs.rendering.EnvRenderer.on_resize | ( | self, | |
| width, | |||
| height | |||
| ) |
Callback function on window resize, overrides inherited method, and updates camera values on top of the inherited on_resize() method. Potential improvements on current behavior: zoom/pan resets on window resize. Args: width (int): new width of window height (int): new height of window Returns: None
| def f110_gym.envs.rendering.EnvRenderer.update_map | ( | self, | |
| map_path, | |||
| map_ext | |||
| ) |
Update the map being drawn by the renderer. Converts image to a list of 3D points representing each obstacle pixel in the map. Args: map_path (str): absolute path to the map without extensions map_ext (str): extension for the map image file Returns: None
| def f110_gym.envs.rendering.EnvRenderer.update_obs | ( | self, | |
| obs | |||
| ) |
Updates the renderer with the latest observation from the gym environment, including the agent poses, and the information text. Args: obs (dict): observation dict from the gym env Returns: None


Autonomous Rover UI (IMU Graphing)
Connecting...
Mission Parameters
Manual Control Active
Sensor Calibration
(0=uncalibrated, 3=fully calibrated)
System = 0
Gyro = 0
Accelerometer = 0
Magnetometer = 0
Sensor Measurements (degrees)
Heading = 0
Roll = 0
Pitch = 0


--- title: Plot seismic wave velocities and polarization directions for aggregates sidebar: documentation_sidebar permalink: CPOSeismicProperties.html folder: documentation toc: false ---
In this section we will calculate the elastic properties of an aggregate and plot its seismic properties in pole figures that can be directly compare to the pole figures for CPO
Let's first import an example dataset from the MTEX toolbox
{% highlight matlab %} mtexdata forsterite {% endhighlight %} {% highlight plaintext %} ebsd = EBSD Phase Orientations Mineral Color Symmetry Crystal reference frame 0 58485 (24%) notIndexed 1 152345 (62%) Forsterite LightSkyBlue mmm 2 26058 (11%) Enstatite DarkSeaGreen mmm 3 9064 (3.7%) Diopside Goldenrod 12/m1 X||a*, Y||b*, Z||c Properties: bands, bc, bs, error, mad, x, y Scan unit : um {% endhighlight %}This dataset consists of the three main phases, forsterite, enstatite and diopside. As we want to plot the seismic properties of this aggregate, we need (i) the modal proportions of each phase in this sample, (ii) their orientations, which is given by their ODFs, (iii) the elastic constants of the minerals and (iv) their densities. One can use the modal proportions that appear in the command window (ol=62 %, en=11%, dio=4%), but there is a lot of non-indexed data. You can recalculate the data only for the indexed data
This EBSD dataset has the foliation N-S, but standard CPO plots and physical properties in geosciences use an external reference frame where the foliation is vertical E-W and the lineation is also E-W but horizontal. We can correct the data by rotating the whole dataset by 90 degree around the z-axis
{% highlight matlab %} ebsd = rotate(ebsd,rotation('axis',-zvector,'angle',90*degree)); plot(ebsd) {% endhighlight %}The elastic stiffness tensor of Forsterite was reported in Abramson et al., 1997 (Journal of Geophysical Research) with respect to the crystal reference frame
{% highlight matlab %} CS_Tensor_Fo = crystalSymmetry('222', [4.762 10.225 5.994],... 'mineral', 'Forsterite', 'color', 'light red'); {% endhighlight %}and the density in g/cm^3
{% highlight matlab %} rho_Fo = 3.3550; {% endhighlight %}by the coefficients \(C_{ij}\) in Voigt matrix notation
{% highlight matlab %} Cij = [[320.5 68.15 71.6 0 0 0];... [ 68.15 196.5 76.8 0 0 0];... [ 71.6 76.8 233.5 0 0 0];... [ 0 0 0 64 0 0];... [ 0 0 0 0 77 0];... [ 0 0 0 0 0 78.7]]; {% endhighlight %}In order to define the stiffness tensor as an MTEX variable we use the command stiffnessTensor.
{% highlight matlab %} C_Fo = stiffnessTensor(Cij,CS_Tensor_Fo,'density',rho_Fo); {% endhighlight %}Note that when defining a single crystal tensor we shall always specify the crystal reference system which has been used to represent the tensor by its coordinates \(c_{ijkl}\).
Now we define the stiffness tensor of enstatite, reported by Chai et al. 1997 (Journal of Geophysical Research)
{% highlight matlab %} % the crystal reference system cs_Tensor_opx = crystalSymmetry('mmm',[ 18.2457 8.7984 5.1959],... [ 90.0000 90.0000 90.0000]*degree,'x||a','z||c',... 'mineral','Enstatite'); % the density rho_opx = 3.3060; % the tensor coefficients Cij =.... [[ 236.90 79.60 63.20 0.00 0.00 0.00];... [ 79.60 180.50 56.80 0.00 0.00 0.00];... [ 63.20 56.80 230.40 0.00 0.00 0.00];... [ 0.00 0.00 0.00 84.30 0.00 0.00];... [ 0.00 0.00 0.00 0.00 79.40 0.00];... [ 0.00 0.00 0.00 0.00 0.00 80.10]]; % define the tensor C_opx = stiffnessTensor(Cij,cs_Tensor_opx,'density',rho_opx); {% endhighlight %}For Diopside coefficients where reported in Isaak et al., 2005 - Physics and Chemistry of Minerals)
{% highlight matlab %} % the crystal reference system cs_Tensor_cpx = crystalSymmetry('121',[9.585 8.776 5.26],... [90.0000 105.8600 90.0000]*degree,'x||a*','z||c',... 'mineral','Diopside'); % the density rho_cpx = 3.2860; % the tensor coefficients Cij =.... [[ 228.10 78.80 70.20 0.00 7.90 0.00];... [ 78.80 181.10 61.10 0.00 5.90 0.00];... [ 70.20 61.10 245.40 0.00 39.70 0.00];... [ 0.00 0.00 0.00 78.90 0.00 6.40];... [ 7.90 5.90 39.70 0.00 68.20 0.00];... [ 0.00 0.00 0.00 6.40 0.00 78.10]]; % define the tensor C_cpx = stiffnessTensor(Cij,cs_Tensor_cpx,'density',rho_cpx); {% endhighlight %}The single crystal seismic velocites can be computed by the command velocity
and are explained in more detail here. At this point we simply use the command plotSeismicVelocities
to get an overview of the single crystal seismic properties.
Combining the EBSD data and the single crystal stiffness tensors we can estimate an bulk stiffness tensor by computing Voigt, Reuss or Hill averages. Tensor averages are explained in more detail in this section. Here we use the command calcTensor
{% highlight matlab %} [CVoigt, CReuss, CHill] = calcTensor(ebsd,C_Fo,C_opx,C_cpx); {% endhighlight %}For visualizing the polycrystal wave velocities we again use the command plotSeismicVelocities
For large data sets the computation of the average stiffness tensor from the EBSD data might be slow. In such cases it can be faster to first estimate an ODF for each individual phase
{% highlight matlab %} odf_ol = calcDensity(ebsd('f').orientations,'halfwidth',10*degree); odf_opx = calcDensity(ebsd('e').orientations,'halfwidth',10*degree); odf_cpx = calcDensity(ebsd('d').orientations,'halfwidth',10*degree); {% endhighlight %}Note that you do don't need to write the full name of each phase, only the initial, that works when phases start with different letters. Also note that although we use an EBSD dataset in this example, you can perform the same calculations with CPO data obtain by other methods (e.g. x-ray/neutron diffraction) as you only need the ODF variable for the calculations
To calculate the average stiffness tensor from the ODFs we first compute them from each phase seperately
{% highlight matlab %} [CVoigt_ol, CReuss_ol, CHill_ol] = mean(C_Fo,odf_ol); [CVoigt_opx, CReuss_opx, CHill_opx] = mean(C_opx,odf_opx); [CVoigt_cpx, CReuss_cpx, CHill_cpx] = mean(C_cpx,odf_cpx); {% endhighlight %}and then take their average weighted according the volume of each phase
{% highlight matlab %} vol_ol = length(ebsd('f')) ./ length(ebsd('indexed')); vol_opx = length(ebsd('e')) ./ length(ebsd('indexed')); vol_cpx = length(ebsd('d')) ./ length(ebsd('indexed')); [CVoigt, CReuss, CHill] = mean([CVoigt_ol, CVoigt_opx, CVoigt_cpx],... 'weights',[vol_ol, vol_opx, vol_cpx]); CHill {% endhighlight %} {% highlight plaintext %} CHill = stiffnessTensor (xyz) density: 3.3449 unit : GPa rank : 4 (3 x 3 x 3 x 3) tensor in Voigt matrix representation: 237.68 75.29 76.92 0.32 2.07 -1.3 75.29 223.44 75.05 1.77 -0.18 -0.87 76.92 75.05 242.28 2.73 2.05 -0.18 0.32 1.77 2.73 78.25 -0.23 0.8 2.07 -0.18 2.05 -0.23 81.68 0.98 -1.3 -0.87 -0.18 0.8 0.98 77.16 {% endhighlight %}Finally, we visualize the polycrystal wave velocities as above
{% highlight matlab %} plotSeismicVelocities(CHill) {% endhighlight %}


Hubic and Duplicity
I mentioned HubiC in my last post, and in it i said that you could use Duplicity for backups. Well, this is how you get it to work…
First, i am using Ubuntu 14.04 (i think…). I use Ubuntu in house for a few things:
- its running Tiernan’s Comms Closet, GeekPhotographer and Tiernan’s Podcast all in house, aswell as being used to build this site. The Web Server and MySQL Server are seperated, MySQL running on Windows, web on Ubuntu… but thats a different story…
- I have a couple of proxy servers running Ubuntu also
- Other general servers running Ubuntu… dont ask, cause i cant remember what they do half the time…
So, Duplicity is a backup application. From their website:
What is it?
Duplicity backs directories by producing encrypted tar-format volumes and uploading them to a remote or local file server. Because duplicity uses librsync, the incremental archives are space efficient and only record the parts of files that have changed since the last backup. Because duplicity uses GnuPG to encrypt and/or sign these archives, they will be safe from spying and/or modification by the server.
The duplicity package also includes the rdiffdir utility. Rdiffdir is an extension of librsync’s rdiff to directories—it can be used to produce signatures and deltas of directories as well as regular files. These signatures and deltas are in GNU tar format.
So, how do we get it working? Well, givin that i am on Ubuntu, these are the steps i needed to do:
- first, we need some credentials and API keys… If you havent signed up for HubiC Do so now…  Terabytes!).
- Login to Hubic, and in the menu go to ‘My Account’, ‘Developers’. in here, create a new application (name and URL to redirect to… http://localhost seems to work correctly). Get the Client ID and Secret ID that was given to you.
- take the contents of the following gist and replace your own details… I know, i am not a fan of sticking my password in a txt file… but it should be your local machine…
- that file should be in your home directory and should be called .hubic_credentials.
- add the duplicity PPA project (https://launchpad.net/~duplicity-team/+archive/ubuntu/ppa) to ubuntu using the add-apt-repository command (details on the link above, under the link ‘read about installing’). for me, i just called ‘sudo add-apt-repository ppa:duplicity-team/ppa’
- install duplicity by doing ‘sudo apt-get install duplicity’. Dont forget (its in the tutorial above!) to do an ‘sudo apt-get update’ first!
- When i ran that, there where a few extra Python packages to be installed, so i was asked did i want to install them… Say, yes.
- Now, to run a backup we run the following command:
duplicity ~/ cf+hubic://location
- cf+hubic is the backend to use, ~/ is the url to backup (my home directory in this case) and location is where on Hubic we want it stored. If this doesent exist, not a problem… it will create it.
- after we run this we… ahhh… i get an error:
BackendException: This backend requires the pyrax library available from Rackspace.
- right… pyrax library is from Rackspace and is available to download though pip…
- I seem to have python and a few other bits installed on this machine, so running ‘sudo pip install pyrax’ works… Your millage may vary… [eg, this is out of scope for this tutorial! your on your own!]
- Other problem… I got a load of weird and wondering errors like this:
AttributeError: 'Module_six_moves_urllib_parse' object has no attribute 'SplitResult'
- I fixed these by running:
sudo pip install furl --upgrade
- FINALLY! ITS ALIVE!!! by default, it asks you for a key for the GnuPG encryption… and its all good! the first backup creates the directories, required files, etc. the next time you run the command, it will only upload changes. it will also ask for your GnuPG code you entered, so remember it!
And thats all folks! Any questions, leave them in the comments!
was last updated: July 5, 2019: fixing all aliases and build issues (41ef786). Found an issue? Improve this page
About
homepage of Tiernan OToole, Geek, photographer, GeekPhotographer, and lazy git.
Recent Posts
- Fixing CID (Caller ID) on incoming calls with 3CX
- Posts
- Network Update Info April 2019
- Adding a Netgear LB2120 to the homelab
- Finally going all in on VoIP
- Auto deploying to multiple servers with GitHub and Webhooks
- Moving the site to Hugo
- Playing with AMD’s Epyc
- AS204994, Own IP Space and Anycast
- Blogging on an iPad Pro


RETROSPECTIVE: DUNE (1984)
A film review by Mike Watson. Copyright 1997 Mike Watson
The following review encompasses two versions of DUNE:
DUNE: THE THEATRICAL VERSION (1984) Runtime: 137 minutes Rating: 2 out of 5 Capsule Review: Cut down to just over two hours by nervous studio executives, the theatrical version of DUNE is a spectacular mess and may be incomprehensible to those unfamiliar with the book. The film's visual splendour, mystical beauty and impressive action scenes only partly compensate for gaping holes in the narrative.
DUNE: THE EXTENDED VERSION (1988) Runtime: 189 minutes Rating: 3 out of 5 Capsule Review: A bit of a throw-together assembled by MCA TV Special Projects for cable television. It was disowned by director David Lynch but it's considerably closer to his original vision by virtue of its improved characterisation and clearer storyline. Quality dubs of this version from the out-of-print Japanese laserdisc release are available from various dealers on the World Wide Web.
***THE REVIEW***
Released in 1984 and made on a then mammoth budget of $40 million, the film of Frank Herbert's cult novel DUNE was eagerly awaited by sci-fi fans. Director David Lynch (BLUE VELVET, ERASERHEAD, TWIN PEAKS) was working on his biggest production to date, a mammoth undertaking filmed under trying conditions on location in Mexico. The screenplay was Lynch's own, chosen after the script submitted by original author Herbert was rejected.
DUNE is set in a universe ruled by powerful families overseen by a successive line of Emperors. The key to cosmic power is the planet Arrakis (Dune), a windswept desert planet that's home to giant sandworms and the precious spice melange. The spice is the most valuable commodity in the universe. It extends the life and expands the consciousness of those who consume it. Most importantly, it allows the navigators of the Spacing Guild (once human but now hideously mutated) to "fold space" and navigate their spacecraft across mammoth distances instantaneously, enabling interstellar commerce and trade to flourish.
Lynch's film by necessity excises parts of the book while retaining the story's two main strands. One is the long-standing rivalry between two families, Houses Atreides and House Harkonnen, and their battle for lucrative mining rights on Arrakis. The second strand is the emergence of young Paul Atreides as the reluctant Messiah long-awaited by the natives of Arrakis, the Fremen. The deeply religious Fremen want control over their homeworld, and young Paul may be the fulfilment of their prophecy that a man would come from the outer worlds and lead them to freedom.
Unfortunately, this epic story unfolds in a confusing and haphazard manner in the theatrical cut of the film, which runs 30 to 60 minutes shorter than what Lynch originally intended. The thinking among Universal's oh-so-wise money men was that films over two hours in duration were not popular with audiences at the time and would not do well at the box office. With Lynch's initial cut running at closer to three or more hours, the studio demanded that further cuts be made. What a great idea! Why not trim down an already complex film so as to make it almost incomprehensible?
 Thus their personalities are vague, their motivations unclear and, in the case of Paul's father Duke Leto, their demise rather meaningless. The end result is a distinct chill: we can't warm to most of the cast and we don't care much for them. And it hardly helps that the voice-over narration is sparse and that the Dune-esque language and terminology sounds like so much gobbledegook to those unfamiliar with the book.
DUNE is also a very serous film. The constant "self-talk" by various characters makes it SO serious and self-absorbed at times that you may find it hard not to wince with embarrassment.  Humour - or at least a gentle kind of humour as distinct from the Harkonnen's mad, sadistic kind - is hard to find. You may balk at the comparison, but as a writer Lynch could well have done with some lessons from George Lucus' STAR WARS trilogy.
The theatrical version is still some way from being a complete disaster, however. It still possesses enough of Lynch's stylistic quirks and enough visual invention to sustain the interest of viewers with a taste for imaginative sci-fi.  Both the production design (Anthony Masters) and costume design (Bob Ringwood) are striking and original. And the magnificent score by Toto and Brian Eno is one of the most underrated soundtracks of the last twenty years. With these elements in place and the benefit of Freddie Francis' lush cinematography, the film is at least a feast for the senses. See it in the widescreen format if you can.
And despite all the cuts, several cast members still make a strong impression, most notably Kenneth McMillan as the supremely nasty Baron Vladimir Harkonnen. Sian Phillips also registers strongly as the Reverend Mother Gaius Helen Mohiam, leader of the Bene Gesserit religious order who's secret aim is to manipulate Paul's destiny for its own shadowy ends. As Paul Atreides, the young Kyle MacLachlan starts off somewhat shakily, but as his character grows in strength so does his performance and he emerges as a credible leader of the Fremen crusade.
The conclusion? Any assessment of this film must take into account that Frank Herbert's original novel is a complex piece of work and presents a tough challenge for any filmmaker. David Lynch took a brave stab at it and, partly due to forces beyond his control, ended up with an officially released version that fails in several key respects. DUNE certainly confused and frustrated a lot of people on its release. Many chose to stay away altogether, as the film's disastrous box office showing attests.
The extended version, however, is a rather different beast. In 1984 Lynch stated his intention to release his own special edition "director's cut" of the film on home video, a clear indication of his dissatisfaction with the version that ended up in the theatres. But, alas, he failed to do so, choosing to move on to other projects. In a way, then, it is partly Lynch's own fault that what appeared instead was an unauthorised extended version, put together in 1988 by MCA TV Special Projects for airing on cable networks in the USA.
Stung into action, Lynch successfully petitioned the Director's Guild to take his name off the credits and replace it with "Allen Smithee", the standard pseudonym for directors who wish to disown their own work. He also had the screenwriting credit changed to the anonymous "Judas Booth".
Certainly, looking at the results of MCA's handiwork there's at least half a dozen instances that, for sheer technical sloppiness, are good enough reasons for the director to object. But these gripes must be considered in light of the improvements that the extended cut of DUNE offers in several crucial areas.
Most of the changes involve the restoration or extension of cut scenes and the addition of extra narration, both of which fill many holes in the original version's storyline. Paul's relationship with his father and associates is more intimate, with moments of humour and warmth lacking previously. The political skulduggery involving the Emperor, the Spacing Guild, the Bene Gesserits and the two warring Houses is far better explained. Paul's initiation into the Fremen way of life on Arrakis is also fleshed out considerably. And as further background, a new prologue has been added featuring narration and painted stills to give us a brief history of the Dune universe.
As a piece of storytelling, then, MCA TV's version of DUNE is clearly superior. As a piece of editing, however, it is at times surprisingly inept. The use of painted stills in the new prologue works well enough, but their occasional appearance once the action begins is inappropriate. There's some sloppy cutting, too, and in a few instances shots even appear out of order. And the use of repeated footage to fabricate certain scenes (eg. ships coming and going, soldiers coming and going) is at times clearly out-of-context. This is the kind of thing to which Lynch objected, and rightly so.
It should also be noted that several questionable scenes and shots from the theatrical version were deleted to satisfy the censorship demands of U.S. television. But the most notable omission is a gratuitous piece of nonsense from Lynch that wasn't even in Herbert's book. The scene features Baron Harkonnen killing a beautiful young man in front of his slobbering henchmen by pulling out his "heart plug". Its a surreal and disturbing episode that's very Lynch-esque but adds nothing to what we already know: the Baron is a nasty piece of work.
Despite its own peculiar flaws, then, the extended version of DUNE is a generally superior film. All up, it contains 35 minutes of restored footage and approximately another 15 minutes of either altered, fabricated or newly created sequences. Unless the idiosyncratic Lynch has a sudden change of heart, the "Alan Smithee" version remains the closest we'll get to what the movie should have been. On repeated viewings, one suspects it is closer than what Lynch would be prepared to admit.
Still, as one of this century's great science-fiction novels, some fans and perhaps the late Herbert himself would argue that DUNE deserved a better fate in its transfer to the screen. With rumours circulating of a new six hour mini-series planned by production company New Amsterdam Entertainment in 1998, it is unlikely that we have heard the last of the Dune saga.
*For background information a special thankyou to<EMAIL_ADDRESS>who maintains the website DUNE: THE ARRAKIS FILE at: http://members.aol.com/Hiphats/arrakis.html
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Music
play_arrow
pause
Brain Breaks
Enter your name:
Save
How long would you like to focus?
0 Min
120 Min
Total Break Time:
Set Time
Choose a Category:
Cats
Dogs
Sports
Dank
Wholesome
_ _ : _ _
plus_one
play_circle_outline
timer_off
timer
Good Job! Now take a Break!
Take my break


My name is John Jacobson. I am a software developer with experience in full stack web development including HTML5, CSS3, JavaScript, jQuery, Bootstrap, and PHP; as well as SQL, Python, Java, and Android development. I have been coding since my dad taught me BASIC on his Apple II Plus when I was 8 years old.
I also have experience as an instructor, teaching martial arts. I have been training in karate since I was 9 and teaching since I was 15. I currently hold the rank of Sandan, or 3rd degree black belt, and I am a certified instructor.


Dear PSC member,
I'm writing to inform you that Article XIII, section 1 (a) of the PSC constitution requires a two- thirds vote of the Delegate Assembly to propose a constitutional amendment. In accordance with this provision, on 6/25/20, a Special DA will be held for the purpose of considering two motions. Effectuating each of the motions would require a constitutional amendment.
Article XIII, section 1 (b) of the PSC constitution states that amendments to the constitution "shall be effective when ratified by a three-fourths (3/4) vote of the Delegate Assembly." Accordingly, if the proposal is successfully proposed by a two-thirds vote at the Special DA, the proposal will be voted on at a regularly scheduled DA later in the same day, 6/25.
The constitution further requires that the "text of any proposed amendment to be voted upon by the Delegate Assembly must be submitted to the Delegates and to the entire membership at least two (2) weeks prior to a vote of the Delegate Assembly." Please see below for the text of the proposed amendments:
Proposal 1: An incumbent Chapter or General Officer, Delegate or Alternate Delegate to the Delegate Assembly or to affiliates who affirms in writing that they sought continued employment in a PSC represented title, and is non-reappointed or otherwise loses their employment with a PSC bargaining unit employer, may retain such office and membership in their previous chapter for up to one year after the effective date of loss of employment provided dues in the annual amount equal to dues paid during the final year on payroll shall be paid directly to the Professional Staff Congress in two (2) six-month installments. The Delegate Assembly, by majority vote, may review and deny the extension on a case by case basis at any time during the course of that year.
Proposal 2: Allow members to restart their full membership rights and benefits up to a year after being separated from CUNY and then rehired.
Thank you for your engagement.
In solidarity,
Nivedita Majumdar Secretary, Professional Staff Congress Associate Professor of English, John Jay College Ph.<PHONE_NUMBER>


reveal.js comes with a few themes built in:
Black (default) -
White -
League -
Sky -
Beige -
Simple
Serif -
Night -
Moon -
Solarized
Mathematics can help understand how behaviour emerges.
TV show from Feb 2008 to Feb 2009 where a jackpot is to be shared between 2 contestants who secretly choose to "split" or "steal".
| Cooperate | Defect | |
| Cooperate | (3,3) | (0,5) |
| Defect | (5,0) | (1,1) |
Always cooperate.
Always defect.
Start by cooperating and then switch.
Play randomly.
Start by cooperating and then do whatever your opponent does in the previous round.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
| Alternator | C | D | C | D | C | D | C | D |
| Tit For Tat | C | C | D | C | D | C | D | C |
| C | D | |
| C | (3,3) | (0,5) |
| D | (5,0) | (1,1) |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
| Alternator | 3 | 5 | 0 | 5 | 0 | 5 | 0 | 5 |
| Tit For Tat | 3 | 0 | 5 | 0 | 5 | 0 | 5 | 0 |
| Alternator | Cooperator | Defector | Random | Tit For Tat | |
| Alternator | 2.0 | 4.0 | 0.5 | 2.3 | 2.5 |
| Cooperator | 1.5 | 3.0 | 0.0 | 1.5 | 3.0 |
| Defector | 3.0 | 5.0 | 1.0 | 3.0 | 1.0 |
| Random | 2.2 | 4.0 | 0.5 | 2.3 | 2.7 |
| Tit For Tat | 2.5 | 3.0 | 1.0 | 2.3 | 3.0 |
(1928-2015)


ARIA Combobox (Simulated, Readonly)
Some random page with a language selector
The Language field is a readonly ARIA Combobox widget that uses simulated button markup.
The simulated combobox is optimized for screen reader and keyboard only users:
- Click the Language button to open the combobox control.
- Within the combobox, press the Down arrow to navigate into the dropdown and use the Up and Down arrow keys to navigate available options.
- Press any letter or number within a readonly field to jump to an option starting with that character or digit.
- Press Enter or Alt+Up to close the dropdown and save the selection.
- To cancel, press Escape, Tab or Shift+Tab to close the dropdown.
- Sighted mouse users can click the desired option to save the selection and close the dropdown.