Drawing Bezier curve with multiple off curve points in PyQt - pyqt

I would like to draw a TrueType Font glyph with PyQt5 QPainterPath. Example glyph fragment: (data from Fonttools ttx )
<pt x="115" y="255" on="1"/>
<pt x="71" y="255" on="0"/>
<pt x="64" y="244" on="0"/>
<pt x="53" y="213" on="0"/>
<pt x="44" y="180" on="0"/>
<pt x="39" y="166" on="1"/>
on=0 means a control point and on=1 means a start/end point
I'm assuming this would not use (QPainterPath) quadTo or cubicTo as it is a higher order curve.

True type fonts actually use only quadratic Bézier curves. This makes sense, they are pretty simple curves that don't require a lot of computation, which is good for performance when you have to potentially draw hundreds or thousands of curves even for a simple paragraph.
After realizing this, I found out strange that you have a curve with 4 control points, but then I did a bit of research and found out this interesting answer.
In reality, the TrueType format allows grouping quadratic curves that always share each start or end point at the middle of each control point.
So, starting with your list:
Start <pt x="115" y="255" on="1"/>
C1 <pt x="71" y="255" on="0"/>
C2 <pt x="64" y="244" on="0"/>
C3 <pt x="53" y="213" on="0"/>
C4 <pt x="44" y="180" on="0"/>
End <pt x="39" y="166" on="1"/>
We have 6 points, but there are 4 curves, and the intermediate points between the 4 control points are the remaining start/end points that exist on the curve:
start
control
end
Start
C1
(C2-C1)/2
(C2-C1)/2
C2
(C3-C2)/2
(C3-C2)/2
C3
(C4-C3)/2
(C4-C3)/2
C4
End
To compute all that, we can cycle through the points and store a reference to the previous, and whenever we have a control point or an on-curve point after them, we add a new quadratic curve to the path.
start
control
end
115 x 255
71 x 255
67.5 x 249.5
67.5 x 249.5
64 x 244
58.5 x 228.5
58.5 x 228.5
53 x 213
48.5 x 106.5
48.5 x 106.5
44 x 180
39 x 166
The following code will create a QPainterPath that corresponds to each <contour> group.
path = QtGui.QPainterPath()
currentCurve = []
started = False
for x, y, onCurve in contour:
point = QtCore.QPointF(x, y)
if onCurve:
if not currentCurve:
# start of curve
currentCurve.append(point)
else:
# end of curve
start, cp = currentCurve
path.quadTo(cp, point)
currentCurve = []
started = False
else:
if len(currentCurve) == 1:
# control point
currentCurve.append(point)
else:
start, cp = currentCurve
# find the midpoint
end = QtCore.QLineF(cp, point).pointAt(.5)
if not started:
# first curve of many
path.moveTo(start)
started = True
path.quadTo(cp, end)
currentCurve = [end, point]

Related

create a 3d cylinder inside 3d volume

I have 3d volume. Which has shape of (399 x 512 x 512). And It has voxel spacing of 0.484704 x 0.484704 x 0.4847
Now, I want to define a cylinder inside this volume with length 5mm, diameter 1mm, intensity 1 inside, intensity 0 outside.
I saw an example to define a cylinder in internet like this code:
from mpl_toolkits.mplot3d import Axes3D
def data_for_cylinder_along_z(center_x,center_y,radius,height_z):
z = np.linspace(0, height_z, 50)
theta = np.linspace(0, 2*np.pi, 50)
theta_grid, z_grid=np.meshgrid(theta, z)
x_grid = radius*np.cos(theta_grid) + center_x
y_grid = radius*np.sin(theta_grid) + center_y
return x_grid,y_grid,z_grid
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xc,Yc,Zc = data_for_cylinder_along_z(0.2,0.2,0.05,0.1)
ax.plot_surface(Xc, Yc, Zc, alpha=0.5)
plt.show()
However, I don't know how to define the cylinder inside the 3d volume keeping all the conditions (length 5mm, diameter 1mm, intensity 1 inside, intensity 0 outside) true. I also want to define the center of cylinder automatically. So that I can define the cylinder at any place of inside the 3d volume keeping the other condition true. Can anyone show or provide any example?
Thanks a lot in advance.
One simple way of solving this would be to perform each of the checks individually and then just keep the voxels that satisfy all of your constraints.
If you build a grid with all of the centers of the voxels: P (399 x 512 x 512 x 3), each voxel at (i,j,k) will be associated with its real-world position (x,y,z).
That's a little tricky, but it should look something like this:
np.stack(np.meshgrid(np.arange(0, shape[0]),
np.arange(0, shape[1]),
np.arange(0, shape[2]), indexing='ij'), axis=3)
If you subtract the cylinder's center (center_x,center_y, center_z), you're left with the relative positions of each (i,j,k) voxel P_rel (399 x 512 x 512 x 3)
When you have that, you can apply each of your tests one after the other. For a Z-oriented cylinder with a radius and height_z it would look something like:
# constrain the Z-axis
not_too_high = P_rel[:,:,:,2]<= (0.5*height_z)
not_too_low = P_rel[:,:,:,2]>= (-0.5*height_z)
# constrain the radial direction
not_too_far = np.linalg.norm(P_rel[:,:,:,:2],axis=3)<=radius
voxels_in_cyl = not_too_high & not_too_low & not_too_far
I haven't tested the code, but you get the idea.
If you wanted to have an cylinder with an arbitrary orientation you would have to project P_rel into axial and radial components and then do an analogous check without "hard-coding" the indices as I did in this example

How do I find the standard deviation of a normal distribution, given mean, threshold, and probability?

Using scipy.stats.norm(), I want to find the standard deviation (scale) that satisfies these requirements:
loc=0, P(X≤-5)=0.2
Until now, I've been solving this through trial and error:
sd = 5.95 # change until requirement is met
stats.norm(loc=0,scale=sd).cdf(-5) # this should eventually reach about 0.2
If it's not possible with scipy.stats.norm(), I'm open to new suggestions.
For your given x and p, the expression that you want is x / norm.ppf(p). (The method ppf is the "percent point function", more commonly called the inverse CDF or the quantile function.)
For example,
In [76]: from scipy.stats import norm
In [77]: x0 = -5
In [78]: p0 = 0.2
In [79]: scale = x0 / norm.ppf(p0)
In [80]: scale
Out[80]: 5.940914749469451
In [81]: norm.cdf(x0, scale=scale)
Out[81]: 0.19999999999999996

How to visualize feasible region for linear programming (with arbitrary inequalities) in Numpy/MatplotLib?

I need to implement a solver for linear programming problems. All of the restrictions are <= ones such as
5x + 10y <= 10
There can be an arbitrary amount of these restrictions. Also , x>=0 y>=0 implicitly.
I need to find the optimal solutions(max) and show the feasible region in matplotlib. I've found the optimal solution by implementing the simplex method but I can't figure out how to draw the graph.
Some approaches I've found:
This link finds the minimum of the y points from each function and uses plt.fillBetween() to draw the region. But it doesn't work when I change the order of the equations. I'm not sure which y values to minimize(). So I can't use it for arbitrary restrictions.
Find solution for every pair of restrictions and draw a polygon. Not efficient.
An easier approach might be to have matplotlib compute the feasible region on its own (with you only providing the constraints) and then simply overlay the "constraint" lines on top.
# plot the feasible region
d = np.linspace(-2,16,300)
x,y = np.meshgrid(d,d)
plt.imshow( ((y>=2) & (2*y<=25-x) & (4*y>=2*x-8) & (y<=2*x-5)).astype(int) ,
extent=(x.min(),x.max(),y.min(),y.max()),origin="lower", cmap="Greys", alpha = 0.3);
# plot the lines defining the constraints
x = np.linspace(0, 16, 2000)
# y >= 2
y1 = (x*0) + 2
# 2y <= 25 - x
y2 = (25-x)/2.0
# 4y >= 2x - 8
y3 = (2*x-8)/4.0
# y <= 2x - 5
y4 = 2 * x -5
# Make plot
plt.plot(x, 2*np.ones_like(y1))
plt.plot(x, y2, label=r'$2y\leq25-x$')
plt.plot(x, y3, label=r'$4y\geq 2x - 8$')
plt.plot(x, y4, label=r'$y\leq 2x-5$')
plt.xlim(0,16)
plt.ylim(0,11)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
This is a vertex enumeration problem. You can use the function lineqs which visualizes the system of inequalities A x >= b for any number of lines. The function will also display the vertices on which the graph was plotted.
The last 2 lines mean that x,y >=0
from intvalpy import lineqs
import numpy as np
A = -np.array([[5, 10],
[-1, 0],
[0, -1]])
b = -np.array([10, 0, 0])
lineqs(A, b, title='Solution', color='gray', alpha=0.5, s=10, size=(15,15), save=False, show=True)
Visual Solution Link

how to find the area of shaded region from the plot using matplotlib [duplicate]

I have a set of points and would like to know if there is a function (for the sake of convenience and probably speed) that can calculate the area enclosed by a set of points.
for example:
x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)
points = zip(x,y)
given points the area should be approximately equal to (pi-2)/4. Maybe there is something from scipy, matplotlib, numpy, shapely, etc. to do this? I won't be encountering any negative values for either the x or y coordinates... and they will be polygons without any defined function.
EDIT:
points will most likely not be in any specified order (clockwise or counterclockwise) and may be quite complex as they are a set of utm coordinates from a shapefile under a set of boundaries
Implementation of Shoelace formula could be done in Numpy. Assuming these vertices:
import numpy as np
x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)
We can redefine the function in numpy to find the area:
def PolyArea(x,y):
return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
And getting results:
print PolyArea(x,y)
# 0.26353377782163534
Avoiding for loop makes this function ~50X faster than PolygonArea:
%timeit PolyArea(x,y)
# 10000 loops, best of 3: 42 µs per loop
%timeit PolygonArea(zip(x,y))
# 100 loops, best of 3: 2.09 ms per loop.
Timing is done in Jupyter notebook.
The most optimized solution that covers all possible cases, would be to use a geometry package, like shapely, scikit-geometry or pygeos. All of them use C++ geometry packages under the hood. The first one is easy to install via pip:
pip install shapely
and simple to use:
from shapely.geometry import Polygon
pgon = Polygon(zip(x, y)) # Assuming the OP's x,y coordinates
print(pgon.area)
To build it from scratch or understand how the underlying algorithm works, check the shoelace formula:
# e.g. corners = [(2.0, 1.0), (4.0, 5.0), (7.0, 8.0)]
def Area(corners):
n = len(corners) # of corners
area = 0.0
for i in range(n):
j = (i + 1) % n
area += corners[i][0] * corners[j][1]
area -= corners[j][0] * corners[i][1]
area = abs(area) / 2.0
return area
Since this works for simple polygons:
If you have a polygon with holes : Calculate the area of the outer ring and subtrack the areas of the inner rings
If you have self-intersecting rings : You have to decompose them into simple sectors
By analysis of Mahdi's answer, I concluded that the majority of time was spent doing np.roll(). By removing the need of the roll, and still using numpy, I got the execution time down to 4-5µs per loop compared to Mahdi's 41µs (for comparison Mahdi's function took an average of 37µs on my machine).
def polygon_area(x,y):
correction = x[-1] * y[0] - y[-1]* x[0]
main_area = np.dot(x[:-1], y[1:]) - np.dot(y[:-1], x[1:])
return 0.5*np.abs(main_area + correction)
By calculating the correctional term, and then slicing the arrays, there is no need to roll or create a new array.
Benchmarks:
10000 iterations
PolyArea(x,y): 37.075µs per loop
polygon_area(x,y): 4.665µs per loop
Timing was done using the time module and time.clock()
maxb's answer gives good performance but can easily lead to loss of precision when coordinate values or the number of points are large. This can be mitigated with a simple coordinate shift:
def polygon_area(x,y):
# coordinate shift
x_ = x - x.mean()
y_ = y - y.mean()
# everything else is the same as maxb's code
correction = x_[-1] * y_[0] - y_[-1]* x_[0]
main_area = np.dot(x_[:-1], y_[1:]) - np.dot(y_[:-1], x_[1:])
return 0.5*np.abs(main_area + correction)
For example, a common geographic reference system is UTM, which might have (x,y) coordinates of (488685.984, 7133035.984). The product of those two values is 3485814708748.448. You can see that this single product is already at the edge of precision (it has the same number of decimal places as the inputs). Adding just a few of these products, let alone thousands, will result in loss of precision.
A simple way to mitigate this is to shift the polygon from large positive coordinates to something closer to (0,0), for example by subtracting the centroid as in the code above. This helps in two ways:
It eliminates a factor of x.mean() * y.mean() from each product
It produces a mix of positive and negative values within each dot product, which will largely cancel.
The coordinate shift does not alter the total area, it just makes the calculation more numerically stable.
It's faster to use shapely.geometry.Polygon rather than to calculate yourself.
from shapely.geometry import Polygon
import numpy as np
def PolyArea(x,y):
return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
coords = np.random.rand(6, 2)
x, y = coords[:, 0], coords[:, 1]
With those codes, and do %timeit:
%timeit PolyArea(x,y)
46.4 µs ± 2.24 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit Polygon(coords).area
20.2 µs ± 414 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
cv2.contourArea() in OpenCV gives an alternative method.
example:
points = np.array([[0,0],[10,0],[10,10],[0,10]])
area = cv2.contourArea(points)
print(area) # 100.0
The argument (points, in the above example) is a numpy array with dtype int, representing the vertices of a polygon: [[x1,y1],[x2,y2], ...]
There's an error in the code above as it doesn't take absolute values on each iteration. The above code will always return zero. (Mathematically, it's the difference between taking signed area or wedge product and the actual area
http://en.wikipedia.org/wiki/Exterior_algebra.) Here's some alternate code.
def area(vertices):
n = len(vertices) # of corners
a = 0.0
for i in range(n):
j = (i + 1) % n
a += abs(vertices[i][0] * vertices[j][1]-vertices[j][0] * vertices[i][1])
result = a / 2.0
return result
a bit late here, but have you considered simply using sympy?
a simple code is :
from sympy import Polygon
a = Polygon((0, 0), (2, 0), (2, 2), (0, 2)).area
print(a)
I compared every solutions offered here to Shapely's area method result, they had the right integer part but the decimal numbers differed. Only #Trenton's solution provided the the correct result.
Now improving on #Trenton's answer to process coordinates as a list of tuples, I came up with the following:
import numpy as np
def polygon_area(coords):
# get x and y in vectors
x = [point[0] for point in coords]
y = [point[1] for point in coords]
# shift coordinates
x_ = x - np.mean(x)
y_ = y - np.mean(y)
# calculate area
correction = x_[-1] * y_[0] - y_[-1] * x_[0]
main_area = np.dot(x_[:-1], y_[1:]) - np.dot(y_[:-1], x_[1:])
return 0.5 * np.abs(main_area + correction)
#### Example output
coords = [(385495.19520441635, 6466826.196947694), (385496.1951836388, 6466826.196947694), (385496.1951836388, 6466825.196929455), (385495.19520441635, 6466825.196929455), (385495.19520441635, 6466826.196947694)]
Shapely's area method: 0.9999974610685296
#Trenton's area method: 0.9999974610685296
This is much simpler, for regular polygons:
import math
def area_polygon(n, s):
return 0.25 * n * s**2 / math.tan(math.pi/n)
since the formula is ¼ n s2 / tan(π/n).
Given the number of sides, n, and the length of each side, s
Based on
https://www.mathsisfun.com/geometry/area-irregular-polygons.html
def _area_(coords):
t=0
for count in range(len(coords)-1):
y = coords[count+1][1] + coords[count][1]
x = coords[count+1][0] - coords[count][0]
z = y * x
t += z
return abs(t/2.0)
a=[(5.09,5.8), (1.68,4.9), (1.48,1.38), (4.76,0.1), (7.0,2.83), (5.09,5.8)]
print _area_(a)
The trick is that the first coordinate should also be last.
def find_int_coordinates(n: int, coords: list[list[int]]) -> float:
rez = 0
x, y = coords[n - 1]
for coord in coords:
rez += (x + coord[0]) * (y - coord[1])
x, y = coord
return abs(rez / 2)

How to get the slope of the endpoints in a hermite spline?

So the slope of a non-endpoint point P1 in a hermite curve is (P2-P0)/2. But how would you get the slope of the endpoints, if I don't want the slope to be 0?
I'm guessing you mean a quadratic Bezier curve which is defined by two end points and one (inner) control point, because the Hermite curve is already defined by tangent vectors (from which the slope is simply Ri y / Ri x, i=0..1, where R0 and R1 are the tangent vectors). Also, the Hermite curve is cubic and has 4 control points, ie. 2 inner control points.
So, for a quadratic Bezier curve, defined by P0, P1, P2, the tangents at the end=points P0 and P3, are just
T0 = P1 - P0
T1 = P2 - P1
So the slopes are
s0 = T0 y / T0 x
s1 = T1 y / T1 x
That's why these curve are so amazingly useful, because they're defined by the features that we want to control for the purposes of design (continuity among segments by placing control points on a straight line through the common endpoint).
The quadratic Bezier can also be considered a degenerate cubic Bezier, where the 2 inner control points coincide (they are the same point); so the first step to convert the "3-point" curve into the Hermite form is to duplicate the middle point producing the cubic Bezier form.
B0 = P0
B1 = P1
B2 = P1
B3 = P2
Then, using equation (13.32) from Foley and Van Dam, Fundamentals of Interactive Computer Graphics, the Hermite form can be produced with a matrix multiplication
G_h = [ [ H_0 ] = [ [ 1 0 0 0 ] [ [ B_0 ] = M_hb G_b
[ H_1 ] [ 0 0 0 1 ] [ B_1 ]
[ T_0 ] [ -3 3 0 0 ] [ B_2 ]
[ T_1 ] ] [ 0 0 -3 3 ] ] [ B_3 ] ]
Ie. The two end-points are the same (H0 = B0, H1 = B3), and the tangent vectors are just weighted sums of the relevant points (T0 = -3*B0 + 3*B1, T1 = -3*B2 + 3*B3).
The tangent vectors here differ in magnitude from the first definitions above, but the directions (and hence, the slope) are the same.

Resources