bspy 3.0.1__py3-none-any.whl → 4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bspy/__init__.py +16 -9
- bspy/_spline_domain.py +83 -47
- bspy/_spline_evaluation.py +44 -62
- bspy/_spline_fitting.py +353 -75
- bspy/_spline_intersection.py +332 -59
- bspy/_spline_operations.py +33 -38
- bspy/hyperplane.py +540 -0
- bspy/manifold.py +391 -0
- bspy/solid.py +839 -0
- bspy/spline.py +310 -77
- bspy/splineOpenGLFrame.py +683 -19
- bspy/viewer.py +795 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/METADATA +25 -13
- bspy-4.1.dist-info/RECORD +17 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/WHEEL +1 -1
- bspy/bspyApp.py +0 -426
- bspy/drawableSpline.py +0 -585
- bspy-3.0.1.dist-info/RECORD +0 -15
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/LICENSE +0 -0
- {bspy-3.0.1.dist-info → bspy-4.1.dist-info}/top_level.txt +0 -0
bspy/splineOpenGLFrame.py
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
from collections import namedtuple
|
|
1
2
|
import numpy as np
|
|
2
|
-
import tkinter as tk
|
|
3
3
|
from OpenGL.GL import *
|
|
4
|
+
from OpenGL.GLU import *
|
|
5
|
+
from bspy.manifold import Manifold
|
|
4
6
|
import OpenGL.GL.shaders as shaders
|
|
5
7
|
try:
|
|
6
8
|
from pyopengltk import OpenGLFrame
|
|
7
9
|
except ImportError:
|
|
8
10
|
from tkinter import Frame as OpenGLFrame
|
|
9
|
-
from bspy import
|
|
10
|
-
from bspy.drawableSpline import _set_color
|
|
11
|
+
from bspy import Spline
|
|
11
12
|
|
|
12
13
|
class SplineOpenGLFrame(OpenGLFrame):
|
|
13
14
|
"""
|
|
14
|
-
A tkinter `OpenGLFrame` with shaders to display a `
|
|
15
|
+
A tkinter `OpenGLFrame` with shaders to display a `Spline`.
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
ROTATE = 1
|
|
@@ -24,6 +25,24 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
24
25
|
MsPerFrame = 50 # Update every 20th of a second
|
|
25
26
|
"""Milliseconds per frame when animating or flying."""
|
|
26
27
|
|
|
28
|
+
maxOrder = 9
|
|
29
|
+
"""Maximum order for drawable splines."""
|
|
30
|
+
maxCoefficients = 120
|
|
31
|
+
"""Maximum number of coefficients for drawable splines."""
|
|
32
|
+
maxKnots = maxCoefficients + maxOrder
|
|
33
|
+
"""Maximum number of knots for drawable splines."""
|
|
34
|
+
_maxFloats = 4 + 2 * maxKnots + 4 * maxCoefficients * maxCoefficients
|
|
35
|
+
"""Maximum total number of floats for drawable splines."""
|
|
36
|
+
|
|
37
|
+
HULL = (1 << 0)
|
|
38
|
+
"""Option to draw the convex hull of the spline (the coefficients). Off by default."""
|
|
39
|
+
SHADED = (1 << 1)
|
|
40
|
+
"""Option to draw the spline shaded (only useful for nInd >= 2). On by default."""
|
|
41
|
+
BOUNDARY = (1 << 2)
|
|
42
|
+
"""Option to draw the boundary of the spline in the line color (only useful for nInd >= 2). On by default."""
|
|
43
|
+
ISOPARMS = (1 << 3)
|
|
44
|
+
"""Option to draw the lines of constant knot values of the spline in the line color (only useful for nInd >= 2). Off by default."""
|
|
45
|
+
|
|
27
46
|
computeBSplineCode = """
|
|
28
47
|
void ComputeBSpline(in int offset, in int order, in int n, in int knot, in float u,
|
|
29
48
|
out float uBSpline[{maxOrder}], out float duBSpline[{maxOrder}])
|
|
@@ -941,6 +960,48 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
941
960
|
discard;
|
|
942
961
|
}
|
|
943
962
|
"""
|
|
963
|
+
|
|
964
|
+
trimmedSurfaceFragmentShaderCode = """
|
|
965
|
+
#version 410 core
|
|
966
|
+
|
|
967
|
+
flat in SplineInfo
|
|
968
|
+
{
|
|
969
|
+
int uOrder, vOrder;
|
|
970
|
+
int uN, vN;
|
|
971
|
+
int uKnot, vKnot;
|
|
972
|
+
float uFirst, vFirst;
|
|
973
|
+
float uSpan, vSpan;
|
|
974
|
+
float u, v;
|
|
975
|
+
float uInterval, vInterval;
|
|
976
|
+
} inData;
|
|
977
|
+
in vec3 worldPosition;
|
|
978
|
+
in vec3 splineColor;
|
|
979
|
+
in vec3 normal;
|
|
980
|
+
in vec2 parameters;
|
|
981
|
+
in vec2 pixelPer;
|
|
982
|
+
|
|
983
|
+
uniform vec4 uFillColor;
|
|
984
|
+
uniform vec4 uLineColor;
|
|
985
|
+
uniform vec3 uLightDirection;
|
|
986
|
+
uniform int uOptions;
|
|
987
|
+
uniform sampler2D uTrimTextureMap;
|
|
988
|
+
|
|
989
|
+
out vec4 color;
|
|
990
|
+
|
|
991
|
+
void main()
|
|
992
|
+
{
|
|
993
|
+
vec2 tex = vec2((parameters.x - inData.uFirst) / inData.uSpan, (parameters.y - inData.vFirst) / inData.vSpan);
|
|
994
|
+
float specular = pow(abs(dot(normal, normalize(uLightDirection + worldPosition / length(worldPosition)))), 25.0);
|
|
995
|
+
bool line = (uOptions & (1 << 2)) > 0 && (pixelPer.x * (parameters.x - inData.uFirst) < 1.5 || pixelPer.x * (inData.uFirst + inData.uSpan - parameters.x) < 1.5);
|
|
996
|
+
line = line || ((uOptions & (1 << 2)) > 0 && (pixelPer.y * (parameters.y - inData.vFirst) < 1.5 || pixelPer.y * (inData.vFirst + inData.vSpan - parameters.y) < 1.5));
|
|
997
|
+
line = line || ((uOptions & (1 << 3)) > 0 && pixelPer.x * (parameters.x - inData.u) < 1.5);
|
|
998
|
+
line = line || ((uOptions & (1 << 3)) > 0 && pixelPer.y * (parameters.y - inData.v) < 1.5);
|
|
999
|
+
color = line ? uLineColor : ((uOptions & (1 << 1)) > 0 ? vec4(splineColor, uFillColor.a) : vec4(0.0, 0.0, 0.0, 0.0));
|
|
1000
|
+
color.rgb = (0.3 + 0.5 * abs(dot(normal, uLightDirection)) + 0.2 * specular) * color.rgb;
|
|
1001
|
+
if (color.a * texture(uTrimTextureMap, tex).r == 0.0)
|
|
1002
|
+
discard;
|
|
1003
|
+
}
|
|
1004
|
+
"""
|
|
944
1005
|
|
|
945
1006
|
def __init__(self, *args, eye=(0.0, 0.0, 3.0), center=(0.0, 0.0, 0.0), up=(0.0, 1.0, 0.0), draw_func=None, **kw):
|
|
946
1007
|
OpenGLFrame.__init__(self, *args, **kw)
|
|
@@ -955,6 +1016,9 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
955
1016
|
self.origin = None
|
|
956
1017
|
self.button = None
|
|
957
1018
|
self.mode = self.ROTATE
|
|
1019
|
+
|
|
1020
|
+
self.computeBSplineCode = self.computeBSplineCode.format(maxOrder=self.maxOrder)
|
|
1021
|
+
self.computeSurfaceSamplesCode = self.computeSurfaceSamplesCode.format(maxOrder=self.maxOrder)
|
|
958
1022
|
|
|
959
1023
|
self.SetBackgroundColor(0.0, 0.2, 0.2)
|
|
960
1024
|
|
|
@@ -967,6 +1031,68 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
967
1031
|
self.bind("<MouseWheel>", self.MouseWheel)
|
|
968
1032
|
self.bind("<Unmap>", self.Unmap)
|
|
969
1033
|
|
|
1034
|
+
@staticmethod
|
|
1035
|
+
def compute_color_vector(r, g=None, b=None, a=None):
|
|
1036
|
+
"""
|
|
1037
|
+
Return an float32 array with the specified color.
|
|
1038
|
+
|
|
1039
|
+
Parameters
|
|
1040
|
+
----------
|
|
1041
|
+
r : `float`, `int` or array-like of floats or ints
|
|
1042
|
+
The red value [0, 1] as a float, [0, 255] as an int, or the rgb or rgba value as floats or ints (default).
|
|
1043
|
+
|
|
1044
|
+
g: `float` or `int`
|
|
1045
|
+
The green value [0, 1] as a float or [0, 255] as an int.
|
|
1046
|
+
|
|
1047
|
+
b: `float` or `int`
|
|
1048
|
+
The blue value [0, 1] as a float or [0, 255] as an int.
|
|
1049
|
+
|
|
1050
|
+
a: `float`, `int`, or None
|
|
1051
|
+
The alpha value [0, 1] as a float or [0, 255] as an int. If `None` then alpha is set to 1.
|
|
1052
|
+
|
|
1053
|
+
Returns
|
|
1054
|
+
-------
|
|
1055
|
+
color : `numpy.array`
|
|
1056
|
+
The specified color as an array of 4 float32 values between 0 and 1.
|
|
1057
|
+
"""
|
|
1058
|
+
if isinstance(r, (int, np.integer)):
|
|
1059
|
+
red = float(r) / 255.0
|
|
1060
|
+
green = red
|
|
1061
|
+
blue = red
|
|
1062
|
+
alpha = 1.0
|
|
1063
|
+
elif np.isscalar(r):
|
|
1064
|
+
red = r
|
|
1065
|
+
green = red
|
|
1066
|
+
blue = red
|
|
1067
|
+
alpha = 1.0
|
|
1068
|
+
elif isinstance(r[0], (int, np.integer)):
|
|
1069
|
+
red = float(r[0]) / 255.0
|
|
1070
|
+
green = float(r[1]) / 255.0
|
|
1071
|
+
blue = float(r[2]) / 255.0
|
|
1072
|
+
alpha = float(r[3]) / 255.0 if len(r) >= 4 else 1.0
|
|
1073
|
+
else:
|
|
1074
|
+
red = r[0]
|
|
1075
|
+
green = r[1]
|
|
1076
|
+
blue = r[2]
|
|
1077
|
+
alpha = r[3] if len(r) >= 4 else 1.0
|
|
1078
|
+
|
|
1079
|
+
if isinstance(g, (int, np.integer)):
|
|
1080
|
+
green = float(g) / 255.0
|
|
1081
|
+
elif np.isscalar(g):
|
|
1082
|
+
green = g
|
|
1083
|
+
|
|
1084
|
+
if isinstance(b, (int, np.integer)):
|
|
1085
|
+
blue = float(b) / 255.0
|
|
1086
|
+
elif np.isscalar(b):
|
|
1087
|
+
blue = b
|
|
1088
|
+
|
|
1089
|
+
if isinstance(a, (int, np.integer)):
|
|
1090
|
+
alpha = float(a) / 255.0
|
|
1091
|
+
elif np.isscalar(a):
|
|
1092
|
+
alpha = a
|
|
1093
|
+
|
|
1094
|
+
return np.array((red, green, blue, alpha), np.float32)
|
|
1095
|
+
|
|
970
1096
|
def SetDefaultView(self, eye, center, up):
|
|
971
1097
|
"""
|
|
972
1098
|
Set the default view values used when resetting the view.
|
|
@@ -994,7 +1120,7 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
994
1120
|
a: `float`, `int`, or None
|
|
995
1121
|
The alpha value [0, 1] as a float or [0, 255] as an int. If `None` then alpha is set to 1.
|
|
996
1122
|
"""
|
|
997
|
-
self.backgroundColor =
|
|
1123
|
+
self.backgroundColor = self.compute_color_vector(r, g, b, a)
|
|
998
1124
|
if self.glInitialized:
|
|
999
1125
|
glClearColor(self.backgroundColor[0], self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3])
|
|
1000
1126
|
|
|
@@ -1035,13 +1161,38 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1035
1161
|
#print("GL_SHADING_LANGUAGE_VERSION: ", glGetString(GL_SHADING_LANGUAGE_VERSION))
|
|
1036
1162
|
#print("GL_MAX_TESS_GEN_LEVEL: ", glGetIntegerv(GL_MAX_TESS_GEN_LEVEL))
|
|
1037
1163
|
|
|
1164
|
+
# Set up frameBuffer into which we draw surface trims.
|
|
1165
|
+
self.frameBuffer = glGenFramebuffers(1)
|
|
1166
|
+
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer)
|
|
1167
|
+
|
|
1168
|
+
# Create the texture buffer for surface trims.
|
|
1169
|
+
self.trimTextureBuffer = glGenTextures(1)
|
|
1170
|
+
glActiveTexture(GL_TEXTURE1)
|
|
1171
|
+
glBindTexture(GL_TEXTURE_2D, self.trimTextureBuffer)
|
|
1172
|
+
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
|
1173
|
+
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
|
|
1174
|
+
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
|
|
1175
|
+
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
1176
|
+
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
1177
|
+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 512, 0, GL_RED, GL_UNSIGNED_BYTE, None)
|
|
1178
|
+
glActiveTexture(GL_TEXTURE0)
|
|
1179
|
+
|
|
1180
|
+
# Attach trim texture buffer to framebuffer and validate framebuffer.
|
|
1181
|
+
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, self.trimTextureBuffer, 0)
|
|
1182
|
+
glDrawBuffers(1, (GL_COLOR_ATTACHMENT0,))
|
|
1183
|
+
if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE:
|
|
1184
|
+
raise ValueError("Framebuffer incomplete")
|
|
1185
|
+
|
|
1186
|
+
# Set framebuffer back to default.
|
|
1187
|
+
glBindFramebuffer(GL_FRAMEBUFFER, 0)
|
|
1188
|
+
|
|
1038
1189
|
# Set up GL texture buffer for spline data
|
|
1039
1190
|
self.splineDataBuffer = glGenBuffers(1)
|
|
1040
1191
|
self.splineTextureBuffer = glGenTextures(1)
|
|
1041
1192
|
glBindBuffer(GL_TEXTURE_BUFFER, self.splineDataBuffer)
|
|
1042
1193
|
glBindTexture(GL_TEXTURE_BUFFER, self.splineTextureBuffer)
|
|
1043
1194
|
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, self.splineDataBuffer)
|
|
1044
|
-
glBufferData(GL_TEXTURE_BUFFER, 4 *
|
|
1195
|
+
glBufferData(GL_TEXTURE_BUFFER, 4 * self._maxFloats, None, GL_STATIC_READ)
|
|
1045
1196
|
|
|
1046
1197
|
# Set light direction
|
|
1047
1198
|
self.lightDirection = np.array((0.63960218, 0.63960218, 0.42640144), np.float32)
|
|
@@ -1053,13 +1204,12 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1053
1204
|
glBufferData(GL_ARRAY_BUFFER, 4 * 4, np.array([0,0,0,0], np.float32), GL_STATIC_DRAW)
|
|
1054
1205
|
|
|
1055
1206
|
# Compile shaders and link programs
|
|
1056
|
-
self.computeBSplineCode = self.computeBSplineCode.format(maxOrder=DrawableSpline.maxOrder)
|
|
1057
|
-
self.computeSurfaceSamplesCode = self.computeSurfaceSamplesCode.format(maxOrder=DrawableSpline.maxOrder)
|
|
1058
1207
|
try:
|
|
1059
1208
|
# Must create CurveProgram first, because it checks and potentially resets tessellationEnabled flag.
|
|
1060
1209
|
self.curveProgram = CurveProgram(self)
|
|
1061
|
-
self.surface3Program = SurfaceProgram(self, 3, "", "", "", "splineColor = uFillColor.rgb;")
|
|
1062
|
-
self.
|
|
1210
|
+
self.surface3Program = SurfaceProgram(self, False, 3, "", "", "", "splineColor = uFillColor.rgb;")
|
|
1211
|
+
self.trimmedSurface3Program = SurfaceProgram(self, True, 3, "", "", "", "splineColor = uFillColor.rgb;")
|
|
1212
|
+
self.surface4Program = SurfaceProgram(self, False, 4,
|
|
1063
1213
|
"""
|
|
1064
1214
|
vec4 kVec = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
1065
1215
|
vec3 pVec;
|
|
@@ -1073,7 +1223,27 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1073
1223
|
pVec = abs(fract(uFillColor.xxx + kVec.xyz) * 6.0 - kVec.www);
|
|
1074
1224
|
splineColor = uFillColor.z * mix(kVec.xxx, clamp(pVec - kVec.xxx, 0.0, 1.0), splineColor.r);
|
|
1075
1225
|
""")
|
|
1076
|
-
self.
|
|
1226
|
+
self.trimmedSurface4Program = SurfaceProgram(self, True, 4,
|
|
1227
|
+
"""
|
|
1228
|
+
vec4 kVec = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
1229
|
+
vec3 pVec;
|
|
1230
|
+
""", "splineColor = vec3(0.0, 0.0, 0.0);",
|
|
1231
|
+
"""
|
|
1232
|
+
splineColor.r += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+3).x;
|
|
1233
|
+
""",
|
|
1234
|
+
# Taken from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
|
|
1235
|
+
# uFillColor is passed in as HSV
|
|
1236
|
+
"""
|
|
1237
|
+
pVec = abs(fract(uFillColor.xxx + kVec.xyz) * 6.0 - kVec.www);
|
|
1238
|
+
splineColor = uFillColor.z * mix(kVec.xxx, clamp(pVec - kVec.xxx, 0.0, 1.0), splineColor.r);
|
|
1239
|
+
""")
|
|
1240
|
+
self.surface6Program = SurfaceProgram(self, False, 6, "", "splineColor = vec3(0.0, 0.0, 0.0);",
|
|
1241
|
+
"""
|
|
1242
|
+
splineColor.r += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+3).x;
|
|
1243
|
+
splineColor.g += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+4).x;
|
|
1244
|
+
splineColor.b += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+5).x;
|
|
1245
|
+
""", "")
|
|
1246
|
+
self.trimmedSurface6Program = SurfaceProgram(self, True, 6, "", "splineColor = vec3(0.0, 0.0, 0.0);",
|
|
1077
1247
|
"""
|
|
1078
1248
|
splineColor.r += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+3).x;
|
|
1079
1249
|
splineColor.g += uBSpline[uB] * vBSpline[vB] * texelFetch(uSplineData, i+4).x;
|
|
@@ -1089,6 +1259,7 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1089
1259
|
print(badLine)
|
|
1090
1260
|
quit()
|
|
1091
1261
|
|
|
1262
|
+
# Set default draw parameters.
|
|
1092
1263
|
glUseProgram(0)
|
|
1093
1264
|
glEnable( GL_DEPTH_TEST )
|
|
1094
1265
|
glClearColor(self.backgroundColor[0], self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3])
|
|
@@ -1106,7 +1277,7 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1106
1277
|
defaultAnchorDistance = np.linalg.norm(self.defaultEye - self.defaultCenter)
|
|
1107
1278
|
clipDistance = defaultAnchorDistance / np.sqrt(3.0)
|
|
1108
1279
|
near = 0.01 * defaultAnchorDistance / 3.0
|
|
1109
|
-
far =
|
|
1280
|
+
far = 3.0 * defaultAnchorDistance
|
|
1110
1281
|
top = clipDistance * near / defaultAnchorDistance # Choose frustum that displays [-clipDistance,clipDistance] in y for z = -defaultAnchorDistance
|
|
1111
1282
|
glFrustum(-top*xExtent, top*xExtent, -top, top, near, far)
|
|
1112
1283
|
#glOrtho(-xExtent, xExtent, -1.0, 1.0, -1.0, 1.0)
|
|
@@ -1117,8 +1288,11 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1117
1288
|
|
|
1118
1289
|
self.curveProgram.ResetBounds(self)
|
|
1119
1290
|
self.surface3Program.ResetBounds(self)
|
|
1291
|
+
self.trimmedSurface3Program.ResetBounds(self)
|
|
1120
1292
|
self.surface4Program.ResetBounds(self)
|
|
1293
|
+
self.trimmedSurface4Program.ResetBounds(self)
|
|
1121
1294
|
self.surface6Program.ResetBounds(self)
|
|
1295
|
+
self.trimmedSurface6Program.ResetBounds(self)
|
|
1122
1296
|
|
|
1123
1297
|
glUseProgram(0)
|
|
1124
1298
|
glMatrixMode(GL_MODELVIEW)
|
|
@@ -1290,6 +1464,488 @@ class SplineOpenGLFrame(OpenGLFrame):
|
|
|
1290
1464
|
self.eye = self.anchorPosition + self.anchorDistance * self.look
|
|
1291
1465
|
self.Update()
|
|
1292
1466
|
|
|
1467
|
+
@staticmethod
|
|
1468
|
+
def make_drawable(spline):
|
|
1469
|
+
"""
|
|
1470
|
+
Ensure a `Spline` can be drawn in a `SplineOpenGLFrame`. Converts 1D splines into 3D drawable curves,
|
|
1471
|
+
2D splines into drawable surfaces (y-axis hold amplitude), and 3D splines into drawable solids.
|
|
1472
|
+
|
|
1473
|
+
Spline surfaces and solids with more than 3 dependent variables will have their added dimensions rendered
|
|
1474
|
+
as colors (up to 6 dependent variables are supported).
|
|
1475
|
+
"""
|
|
1476
|
+
if not(isinstance(spline, Spline)): raise ValueError("Invalid spline")
|
|
1477
|
+
if spline.nInd > 3: raise ValueError("Spline must have no more than 3 independent variables")
|
|
1478
|
+
if spline.nDep > 6: raise ValueError("Spline must have no more than 6 dependent variables")
|
|
1479
|
+
|
|
1480
|
+
if not hasattr(spline, "cache"):
|
|
1481
|
+
spline.cache = {}
|
|
1482
|
+
|
|
1483
|
+
if not "knots32" in spline.cache:
|
|
1484
|
+
knotList = [knots.astype(np.float32, copy=False) for knots in spline.knots]
|
|
1485
|
+
spline.cache["knots32"] = knotList # Shaders expect float32 knots
|
|
1486
|
+
|
|
1487
|
+
if not "coefs32" in spline.cache:
|
|
1488
|
+
nDep = 3
|
|
1489
|
+
if spline.nInd >= 2 and spline.nDep > 3:
|
|
1490
|
+
nDep = 4 if spline.nDep == 4 else 6 # No nDep of 5
|
|
1491
|
+
|
|
1492
|
+
coefs = np.zeros((nDep, *spline.nCoef), np.float32)
|
|
1493
|
+
if spline.nInd == 1:
|
|
1494
|
+
if spline.nDep == 1:
|
|
1495
|
+
graph = spline.graph()
|
|
1496
|
+
coefs[0] = graph.coefs[0]
|
|
1497
|
+
coefs[1] = graph.coefs[1]
|
|
1498
|
+
else:
|
|
1499
|
+
coefs[:min(spline.nDep, 3)] = spline.coefs[:min(spline.nDep, 3)]
|
|
1500
|
+
elif spline.nInd == 2:
|
|
1501
|
+
if spline.nDep == 1:
|
|
1502
|
+
graph = spline.graph()
|
|
1503
|
+
coefs[0] = graph.coefs[0]
|
|
1504
|
+
coefs[1] = graph.coefs[2]
|
|
1505
|
+
coefs[2] = graph.coefs[1]
|
|
1506
|
+
else:
|
|
1507
|
+
coefs[:spline.nDep] = spline.coefs
|
|
1508
|
+
# For dimensions above three, rescale dependent variables to [0, 1].
|
|
1509
|
+
for i in range(3, spline.nDep):
|
|
1510
|
+
minCoef = coefs[i].min()
|
|
1511
|
+
rangeCoef = coefs[i].max() - minCoef
|
|
1512
|
+
if rangeCoef > 1.0e-8:
|
|
1513
|
+
coefs[i] = (coefs[i] - minCoef) / rangeCoef
|
|
1514
|
+
else:
|
|
1515
|
+
coefs[i] = max(0.0, min(1.0, minCoef))
|
|
1516
|
+
elif spline.nInd == 3:
|
|
1517
|
+
if spline.nDep == 1:
|
|
1518
|
+
graph = spline.graph()
|
|
1519
|
+
coefs[0] = graph.coefs[0]
|
|
1520
|
+
coefs[1] = graph.coefs[3]
|
|
1521
|
+
coefs[2] = graph.coefs[1]
|
|
1522
|
+
coefs[3] = graph.coefs[2]
|
|
1523
|
+
else:
|
|
1524
|
+
coefs[:spline.nDep] = spline.coefs
|
|
1525
|
+
# For dimensions above three, rescale dependent variables to [0, 1].
|
|
1526
|
+
for i in range(3, spline.nDep):
|
|
1527
|
+
minCoef = coefs[i].min()
|
|
1528
|
+
rangeCoef = coefs[i].max() - minCoef
|
|
1529
|
+
if rangeCoef > 1.0e-8:
|
|
1530
|
+
coefs[i] = (coefs[i] - minCoef) / rangeCoef
|
|
1531
|
+
else:
|
|
1532
|
+
coefs[i] = max(0.0, min(1.0, minCoef))
|
|
1533
|
+
else:
|
|
1534
|
+
raise ValueError("Can't convert to drawable spline.")
|
|
1535
|
+
|
|
1536
|
+
spline.cache["coefs32"] = coefs.T # Shaders expect transpose of float32 coefs
|
|
1537
|
+
|
|
1538
|
+
if not "fillColor" in spline.metadata:
|
|
1539
|
+
spline.metadata["fillColor"] = np.array((0.0, 1.0, 0.0, 1.0), np.float32)
|
|
1540
|
+
if not "lineColor" in spline.metadata:
|
|
1541
|
+
spline.metadata["lineColor"] = np.array((0.0, 0.0, 0.0, 1.0) if spline.nInd > 1 else (1.0, 1.0, 1.0, 1.0), np.float32)
|
|
1542
|
+
if not "options" in spline.metadata:
|
|
1543
|
+
spline.metadata["options"] = SplineOpenGLFrame.SHADED | SplineOpenGLFrame.BOUNDARY
|
|
1544
|
+
if not "animate" in spline.metadata:
|
|
1545
|
+
spline.metadata["animate"] = None
|
|
1546
|
+
|
|
1547
|
+
@staticmethod
|
|
1548
|
+
def tessellate2DSolid(solid):
|
|
1549
|
+
"""
|
|
1550
|
+
Returns an array of triangles that tessellate the given 2D solid
|
|
1551
|
+
"""
|
|
1552
|
+
assert solid.dimension == 2
|
|
1553
|
+
assert solid.containsInfinity == False
|
|
1554
|
+
|
|
1555
|
+
# First, collect all manifold contour endpoints, accounting for slight numerical error.
|
|
1556
|
+
class Endpoint:
|
|
1557
|
+
def __init__(self, curve, t, clockwise, isStart, otherEnd=None):
|
|
1558
|
+
self.curve = curve
|
|
1559
|
+
self.t = t
|
|
1560
|
+
self.xy = curve.manifold.evaluate((t,))
|
|
1561
|
+
self.clockwise = clockwise
|
|
1562
|
+
self.isStart = isStart
|
|
1563
|
+
self.otherEnd = otherEnd
|
|
1564
|
+
self.connection = None
|
|
1565
|
+
endpoints = []
|
|
1566
|
+
for curve in solid.boundaries:
|
|
1567
|
+
curve.domain.boundaries.sort(key=lambda boundary: (boundary.manifold.evaluate(0.0), -boundary.manifold.normal(0.0)))
|
|
1568
|
+
leftB = 0
|
|
1569
|
+
rightB = 0
|
|
1570
|
+
boundaryCount = len(curve.domain.boundaries)
|
|
1571
|
+
while leftB < boundaryCount:
|
|
1572
|
+
if curve.domain.boundaries[leftB].manifold.normal(0.0) < 0.0:
|
|
1573
|
+
leftPoint = curve.domain.boundaries[leftB].manifold.evaluate(0.0)[0]
|
|
1574
|
+
while rightB < boundaryCount:
|
|
1575
|
+
rightPoint = curve.domain.boundaries[rightB].manifold.evaluate(0.0)[0]
|
|
1576
|
+
if leftPoint - Manifold.minSeparation < rightPoint and curve.domain.boundaries[rightB].manifold.normal(0.0) > 0.0:
|
|
1577
|
+
t = curve.manifold.tangent_space(leftPoint)[:,0]
|
|
1578
|
+
n = curve.manifold.normal(leftPoint)
|
|
1579
|
+
clockwise = t[0] * n[1] - t[1] * n[0] > 0.0
|
|
1580
|
+
ep1 = Endpoint(curve, leftPoint, clockwise, rightPoint >= leftPoint)
|
|
1581
|
+
ep2 = Endpoint(curve, rightPoint, clockwise, rightPoint < leftPoint, ep1)
|
|
1582
|
+
ep1.otherEnd = ep2
|
|
1583
|
+
endpoints.append(ep1)
|
|
1584
|
+
endpoints.append(ep2)
|
|
1585
|
+
leftB = rightB
|
|
1586
|
+
rightB += 1
|
|
1587
|
+
break
|
|
1588
|
+
rightB += 1
|
|
1589
|
+
leftB += 1
|
|
1590
|
+
|
|
1591
|
+
# Second, collect all valid pairings of endpoints (normal not flipped between segments).
|
|
1592
|
+
Connection = namedtuple('Connection', ('distance', 'ep1', 'ep2'))
|
|
1593
|
+
connections = []
|
|
1594
|
+
for i, ep1 in enumerate(endpoints[:-1]):
|
|
1595
|
+
for ep2 in endpoints[i+1:]:
|
|
1596
|
+
if (ep1.clockwise == ep2.clockwise and ep1.isStart != ep2.isStart) or \
|
|
1597
|
+
(ep1.clockwise != ep2.clockwise and ep1.isStart == ep2.isStart):
|
|
1598
|
+
connections.append(Connection(np.linalg.norm(ep1.xy - ep2.xy), ep1, ep2))
|
|
1599
|
+
|
|
1600
|
+
# Third, only keep closest pairings (prune the rest).
|
|
1601
|
+
connections.sort(key=lambda connection: -connection.distance)
|
|
1602
|
+
while connections:
|
|
1603
|
+
connection = connections.pop()
|
|
1604
|
+
connection.ep1.connection = connection.ep2
|
|
1605
|
+
connection.ep2.connection = connection.ep1
|
|
1606
|
+
connections = [c for c in connections if c.ep1 is not connection.ep1 and c.ep1 is not connection.ep2 and \
|
|
1607
|
+
c.ep2 is not connection.ep1 and c.ep2 is not connection.ep2]
|
|
1608
|
+
|
|
1609
|
+
# Fourth, set up GLUT to tesselate the solid.
|
|
1610
|
+
tess = gluNewTess()
|
|
1611
|
+
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD)
|
|
1612
|
+
vertices = []
|
|
1613
|
+
def beginCallback(type=None):
|
|
1614
|
+
vertices = []
|
|
1615
|
+
def edgeFlagDataCallback(flag, polygonData):
|
|
1616
|
+
pass # Forces triangulation of polygons rather than triangle fans or strips
|
|
1617
|
+
def vertexCallback(vertex, otherData=None):
|
|
1618
|
+
vertices.append(vertex[:2])
|
|
1619
|
+
def combineCallback(vertex, neighbors, neighborWeights, outData=None):
|
|
1620
|
+
outData = vertex
|
|
1621
|
+
return outData
|
|
1622
|
+
def endCallback():
|
|
1623
|
+
pass
|
|
1624
|
+
gluTessCallback(tess, GLU_TESS_BEGIN, beginCallback)
|
|
1625
|
+
gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, edgeFlagDataCallback)
|
|
1626
|
+
gluTessCallback(tess, GLU_TESS_VERTEX, vertexCallback)
|
|
1627
|
+
gluTessCallback(tess, GLU_TESS_COMBINE, combineCallback)
|
|
1628
|
+
gluTessCallback(tess, GLU_TESS_END, endCallback)
|
|
1629
|
+
|
|
1630
|
+
# Fifth, trace the contours from pairing to pairing, using GLUT to tesselate the interior.
|
|
1631
|
+
gluTessBeginPolygon(tess, 0)
|
|
1632
|
+
while endpoints:
|
|
1633
|
+
start = endpoints[0]
|
|
1634
|
+
if not start.isStart:
|
|
1635
|
+
start = start.otherEnd
|
|
1636
|
+
# Run backwards until you hit start again or hit an end.
|
|
1637
|
+
if start.connection is not None:
|
|
1638
|
+
originalStart = start
|
|
1639
|
+
next = start.connection
|
|
1640
|
+
start = None
|
|
1641
|
+
while next is not None and start is not originalStart:
|
|
1642
|
+
start = next.otherEnd
|
|
1643
|
+
next = start.connection
|
|
1644
|
+
# Run forwards submitting vertices for the contour.
|
|
1645
|
+
next = start
|
|
1646
|
+
gluTessBeginContour(tess)
|
|
1647
|
+
while next is not None:
|
|
1648
|
+
endpoints.remove(next)
|
|
1649
|
+
endpoints.remove(next.otherEnd)
|
|
1650
|
+
subdivisions = max(int(abs(next.otherEnd.t - next.t) / 0.1), 20) if isinstance(next.curve.manifold, Spline) else 2
|
|
1651
|
+
for t in np.linspace(next.t, next.otherEnd.t, subdivisions):
|
|
1652
|
+
xy = next.curve.manifold.evaluate((t,))
|
|
1653
|
+
vertex = (*xy, 0.0)
|
|
1654
|
+
gluTessVertex(tess, vertex, vertex)
|
|
1655
|
+
next = next.otherEnd.connection
|
|
1656
|
+
if next is start:
|
|
1657
|
+
break
|
|
1658
|
+
gluTessEndContour(tess)
|
|
1659
|
+
gluTessEndPolygon(tess)
|
|
1660
|
+
gluDeleteTess(tess)
|
|
1661
|
+
return np.array(vertices, np.float32)
|
|
1662
|
+
|
|
1663
|
+
def _DrawPoints(self, spline, drawCoefficients):
|
|
1664
|
+
"""
|
|
1665
|
+
Draw spline points for an nInd == 0 or order == 1 spline within a `SplineOpenGLFrame`. The self will call this method for you.
|
|
1666
|
+
"""
|
|
1667
|
+
glColor4fv(spline.metadata["lineColor"])
|
|
1668
|
+
glBegin(GL_POINTS)
|
|
1669
|
+
if spline.nInd == 0:
|
|
1670
|
+
glVertex3fv(drawCoefficients)
|
|
1671
|
+
else:
|
|
1672
|
+
for point in drawCoefficients:
|
|
1673
|
+
glVertex3fv(point)
|
|
1674
|
+
glEnd()
|
|
1675
|
+
|
|
1676
|
+
def _DrawCurve(self, spline, drawCoefficients):
|
|
1677
|
+
"""
|
|
1678
|
+
Draw a spline curve (nInd == 1) within a `SplineOpenGLFrame`. The self will call this method for you.
|
|
1679
|
+
"""
|
|
1680
|
+
if spline.metadata["options"] & self.HULL:
|
|
1681
|
+
glColor3f(0.0, 0.0, 1.0)
|
|
1682
|
+
glBegin(GL_LINE_STRIP)
|
|
1683
|
+
for point in drawCoefficients:
|
|
1684
|
+
glVertex3f(point[0], point[1], point[2])
|
|
1685
|
+
glEnd()
|
|
1686
|
+
|
|
1687
|
+
program = self.curveProgram
|
|
1688
|
+
glUseProgram(program.curveProgram)
|
|
1689
|
+
glUniform4fv(program.uCurveLineColor, 1, spline.metadata["lineColor"])
|
|
1690
|
+
glBindBuffer(GL_TEXTURE_BUFFER, self.splineDataBuffer)
|
|
1691
|
+
offset = 0
|
|
1692
|
+
size = 4 * 2
|
|
1693
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, np.array((spline.order[0], spline.nCoef[0]), np.float32))
|
|
1694
|
+
offset += size
|
|
1695
|
+
knots = spline.cache["knots32"]
|
|
1696
|
+
size = 4 * len(knots[0])
|
|
1697
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, knots[0])
|
|
1698
|
+
offset += size
|
|
1699
|
+
size = 3 * 4 * spline.nCoef[0]
|
|
1700
|
+
drawCoefficients = drawCoefficients[..., :3]
|
|
1701
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, drawCoefficients)
|
|
1702
|
+
glEnableVertexAttribArray(program.aCurveParameters)
|
|
1703
|
+
if self.tessellationEnabled:
|
|
1704
|
+
glPatchParameteri(GL_PATCH_VERTICES, 1)
|
|
1705
|
+
glDrawArraysInstanced(GL_PATCHES, 0, 1, spline.nCoef[0] - spline.order[0] + 1)
|
|
1706
|
+
else:
|
|
1707
|
+
glDrawArraysInstanced(GL_POINTS, 0, 1, spline.nCoef[0] - spline.order[0] + 1)
|
|
1708
|
+
glFlush() # Old graphics card
|
|
1709
|
+
glDisableVertexAttribArray(program.aCurveParameters)
|
|
1710
|
+
glUseProgram(0)
|
|
1711
|
+
|
|
1712
|
+
@staticmethod
|
|
1713
|
+
def ConvertRGBToHSV(r, g, b, a):
|
|
1714
|
+
# Taken from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
|
|
1715
|
+
K = 0.0
|
|
1716
|
+
if g < b:
|
|
1717
|
+
tmp = g
|
|
1718
|
+
g = b
|
|
1719
|
+
b = tmp
|
|
1720
|
+
K = -1.0
|
|
1721
|
+
if r < g:
|
|
1722
|
+
tmp = r
|
|
1723
|
+
r = g
|
|
1724
|
+
g = tmp
|
|
1725
|
+
K = -2.0 / 6.0 - K
|
|
1726
|
+
chroma = r - min(g, b)
|
|
1727
|
+
return np.array((abs(K + (g - b) / (6.0 * chroma + 1e-20)), chroma / (r + 1e-20), r, a), np.float32)
|
|
1728
|
+
|
|
1729
|
+
def _DrawSurface(self, spline, drawCoefficients):
|
|
1730
|
+
"""
|
|
1731
|
+
Draw a spline surface (nInd == 2) within a `SplineOpenGLFrame`.
|
|
1732
|
+
"""
|
|
1733
|
+
if spline.metadata["options"] & self.HULL:
|
|
1734
|
+
glColor3f(0.0, 0.0, 1.0)
|
|
1735
|
+
for pointList in drawCoefficients:
|
|
1736
|
+
glBegin(GL_LINE_STRIP)
|
|
1737
|
+
for point in pointList:
|
|
1738
|
+
glVertex3f(point[0], point[1], point[2])
|
|
1739
|
+
glEnd()
|
|
1740
|
+
|
|
1741
|
+
fillColor = spline.metadata["fillColor"]
|
|
1742
|
+
if spline.nDep <= 3:
|
|
1743
|
+
nDep = 3
|
|
1744
|
+
program = self.trimmedSurface3Program if "trim" in spline.cache else self.surface3Program
|
|
1745
|
+
elif spline.nDep == 4:
|
|
1746
|
+
nDep = 4
|
|
1747
|
+
program = self.trimmedSurface4Program if "trim" in spline.cache else self.surface4Program
|
|
1748
|
+
program = self.surface4Program
|
|
1749
|
+
fillColor = self.ConvertRGBToHSV(fillColor[0], fillColor[1], fillColor[2], fillColor[3])
|
|
1750
|
+
elif spline.nDep <= 6:
|
|
1751
|
+
nDep = 6
|
|
1752
|
+
program = self.trimmedSurface6Program if "trim" in spline.cache else self.surface6Program
|
|
1753
|
+
else:
|
|
1754
|
+
raise ValueError("Can't draw surface.")
|
|
1755
|
+
|
|
1756
|
+
useBlending = fillColor[3] < 1.0
|
|
1757
|
+
if useBlending:
|
|
1758
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
1759
|
+
glEnable( GL_BLEND )
|
|
1760
|
+
glDisable( GL_DEPTH_TEST )
|
|
1761
|
+
|
|
1762
|
+
glUseProgram(program.surfaceProgram)
|
|
1763
|
+
glUniform4fv(program.uSurfaceFillColor, 1, fillColor)
|
|
1764
|
+
glUniform4fv(program.uSurfaceLineColor, 1, spline.metadata["lineColor"])
|
|
1765
|
+
glUniform1i(program.uSurfaceOptions, spline.metadata["options"])
|
|
1766
|
+
glBindBuffer(GL_TEXTURE_BUFFER, self.splineDataBuffer)
|
|
1767
|
+
offset = 0
|
|
1768
|
+
size = 4 * 4
|
|
1769
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, np.array((spline.order[0], spline.order[1], spline.nCoef[0], spline.nCoef[1]), np.float32))
|
|
1770
|
+
offset += size
|
|
1771
|
+
knots = spline.cache["knots32"]
|
|
1772
|
+
size = 4 * len(knots[0])
|
|
1773
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, knots[0])
|
|
1774
|
+
offset += size
|
|
1775
|
+
size = 4 * len(knots[1])
|
|
1776
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, knots[1])
|
|
1777
|
+
offset += size
|
|
1778
|
+
size = nDep * 4 * spline.nCoef[0] * spline.nCoef[1]
|
|
1779
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, drawCoefficients)
|
|
1780
|
+
glEnableVertexAttribArray(program.aSurfaceParameters)
|
|
1781
|
+
if self.tessellationEnabled:
|
|
1782
|
+
glPatchParameteri(GL_PATCH_VERTICES, 1)
|
|
1783
|
+
glDrawArraysInstanced(GL_PATCHES, 0, 1, (spline.nCoef[0] - spline.order[0] + 1) * (spline.nCoef[1] - spline.order[1] + 1))
|
|
1784
|
+
else:
|
|
1785
|
+
glDrawArraysInstanced(GL_POINTS, 0, 1, (spline.nCoef[0] - spline.order[0] + 1) * (spline.nCoef[1] - spline.order[1] + 1))
|
|
1786
|
+
glFlush() # Old graphics card
|
|
1787
|
+
glDisableVertexAttribArray(program.aSurfaceParameters)
|
|
1788
|
+
glUseProgram(0)
|
|
1789
|
+
if useBlending:
|
|
1790
|
+
glDisable( GL_BLEND )
|
|
1791
|
+
glEnable( GL_DEPTH_TEST )
|
|
1792
|
+
|
|
1793
|
+
def _DrawSolid(self, spline, drawCoefficients):
|
|
1794
|
+
"""
|
|
1795
|
+
Draw a spline solid (nInd == 3) within a `SplineOpenGLFrame`.
|
|
1796
|
+
"""
|
|
1797
|
+
if spline.metadata["options"] & self.HULL:
|
|
1798
|
+
glColor3f(0.0, 0.0, 1.0)
|
|
1799
|
+
for pointSet in drawCoefficients:
|
|
1800
|
+
for pointList in pointSet:
|
|
1801
|
+
glBegin(GL_LINE_STRIP)
|
|
1802
|
+
for point in pointList:
|
|
1803
|
+
glVertex3f(point[0], point[1], point[2])
|
|
1804
|
+
glEnd()
|
|
1805
|
+
|
|
1806
|
+
fillColor = spline.metadata["fillColor"].copy()
|
|
1807
|
+
lineColor = spline.metadata["lineColor"].copy()
|
|
1808
|
+
if spline.nDep <= 3:
|
|
1809
|
+
nDep = 3
|
|
1810
|
+
program = self.trimmedSurface3Program if "trim" in spline.cache else self.surface3Program
|
|
1811
|
+
elif spline.nDep == 4:
|
|
1812
|
+
nDep = 4
|
|
1813
|
+
program = self.trimmedSurface4Program if "trim" in spline.cache else self.surface4Program
|
|
1814
|
+
program = self.surface4Program
|
|
1815
|
+
fillColor = self.ConvertRGBToHSV(fillColor[0], fillColor[1], fillColor[2], fillColor[3])
|
|
1816
|
+
elif spline.nDep <= 6:
|
|
1817
|
+
nDep = 6
|
|
1818
|
+
program = self.trimmedSurface6Program if "trim" in spline.cache else self.surface6Program
|
|
1819
|
+
else:
|
|
1820
|
+
raise ValueError("Can't draw surface.")
|
|
1821
|
+
fillColor[3] *= 0.5
|
|
1822
|
+
lineColor[3] *= 0.5
|
|
1823
|
+
knots = spline.cache["knots32"]
|
|
1824
|
+
|
|
1825
|
+
def _DrawBoundarySurface(axis, index):
|
|
1826
|
+
fullSlice = slice(None)
|
|
1827
|
+
if axis == 0:
|
|
1828
|
+
i1 = 1
|
|
1829
|
+
i2 = 2
|
|
1830
|
+
coefSlice = (fullSlice, fullSlice, index, fullSlice)
|
|
1831
|
+
elif axis == 1:
|
|
1832
|
+
i1 = 0
|
|
1833
|
+
i2 = 2
|
|
1834
|
+
coefSlice = (fullSlice, index, fullSlice, fullSlice)
|
|
1835
|
+
else:
|
|
1836
|
+
i1 = 0
|
|
1837
|
+
i2 = 1
|
|
1838
|
+
coefSlice = (index, fullSlice, fullSlice, fullSlice)
|
|
1839
|
+
|
|
1840
|
+
glBindBuffer(GL_TEXTURE_BUFFER, self.splineDataBuffer)
|
|
1841
|
+
offset = 0
|
|
1842
|
+
size = 4 * 4
|
|
1843
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, np.array((spline.order[i1], spline.order[i2], spline.nCoef[i1], spline.nCoef[i2]), np.float32))
|
|
1844
|
+
offset += size
|
|
1845
|
+
size = 4 * len(knots[i1])
|
|
1846
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, knots[i1])
|
|
1847
|
+
offset += size
|
|
1848
|
+
size = 4 * len(knots[i2])
|
|
1849
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, knots[i2])
|
|
1850
|
+
offset += size
|
|
1851
|
+
size = nDep * 4 * spline.nCoef[i1] * spline.nCoef[i2]
|
|
1852
|
+
glBufferSubData(GL_TEXTURE_BUFFER, offset, size, drawCoefficients[coefSlice])
|
|
1853
|
+
glEnableVertexAttribArray(program.aSurfaceParameters)
|
|
1854
|
+
if self.tessellationEnabled:
|
|
1855
|
+
glPatchParameteri(GL_PATCH_VERTICES, 1)
|
|
1856
|
+
glDrawArraysInstanced(GL_PATCHES, 0, 1, (spline.nCoef[i1] - spline.order[i1] + 1) * (spline.nCoef[i2] - spline.order[i2] + 1))
|
|
1857
|
+
else:
|
|
1858
|
+
glDrawArraysInstanced(GL_POINTS, 0, 1, (spline.nCoef[i1] - spline.order[i1] + 1) * (spline.nCoef[i2] - spline.order[i2] + 1))
|
|
1859
|
+
glFlush() # Old graphics card
|
|
1860
|
+
glDisableVertexAttribArray(program.aSurfaceParameters)
|
|
1861
|
+
|
|
1862
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
1863
|
+
glEnable( GL_BLEND )
|
|
1864
|
+
glDisable( GL_DEPTH_TEST )
|
|
1865
|
+
glUseProgram(program.surfaceProgram)
|
|
1866
|
+
glUniform4fv(program.uSurfaceFillColor, 1, fillColor)
|
|
1867
|
+
glUniform4fv(program.uSurfaceLineColor, 1, lineColor)
|
|
1868
|
+
glUniform1i(program.uSurfaceOptions, spline.metadata["options"])
|
|
1869
|
+
|
|
1870
|
+
_DrawBoundarySurface(0, 0)
|
|
1871
|
+
_DrawBoundarySurface(0, -1)
|
|
1872
|
+
_DrawBoundarySurface(1, 0)
|
|
1873
|
+
_DrawBoundarySurface(1, -1)
|
|
1874
|
+
_DrawBoundarySurface(2, 0)
|
|
1875
|
+
_DrawBoundarySurface(2, -1)
|
|
1876
|
+
|
|
1877
|
+
glUseProgram(0)
|
|
1878
|
+
glDisable( GL_BLEND )
|
|
1879
|
+
glEnable( GL_DEPTH_TEST )
|
|
1880
|
+
|
|
1881
|
+
def DrawSpline(self, spline, transform):
|
|
1882
|
+
"""
|
|
1883
|
+
Draw a spline within a `SplineOpenGLFrame`.
|
|
1884
|
+
"""
|
|
1885
|
+
# Fill trim stencil.
|
|
1886
|
+
if "trim" in spline.cache:
|
|
1887
|
+
# Draw trim tessellation into trim texture framebuffer.
|
|
1888
|
+
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer)
|
|
1889
|
+
glDisable(GL_DEPTH_TEST)
|
|
1890
|
+
glViewport(0,0,512,512)
|
|
1891
|
+
glClearColor(0.0, 0.0, 0.0, 1.0)
|
|
1892
|
+
glClear(GL_COLOR_BUFFER_BIT)
|
|
1893
|
+
glMatrixMode(GL_PROJECTION)
|
|
1894
|
+
glLoadIdentity()
|
|
1895
|
+
bounds = spline.domain()
|
|
1896
|
+
glOrtho(bounds[0, 0], bounds[0, 1], bounds[1, 0], bounds[1, 1], -1.0, 1.0)
|
|
1897
|
+
glColor3f(1.0, 0.0, 0.0)
|
|
1898
|
+
glBegin(GL_TRIANGLES)
|
|
1899
|
+
for vertex in spline.cache["trim"]:
|
|
1900
|
+
glVertex2fv(vertex)
|
|
1901
|
+
glEnd()
|
|
1902
|
+
glFlush()
|
|
1903
|
+
# Reset view for main framebuffer.
|
|
1904
|
+
glBindFramebuffer(GL_FRAMEBUFFER, 0)
|
|
1905
|
+
glEnable(GL_DEPTH_TEST)
|
|
1906
|
+
glViewport(0, 0, self.width, self.height)
|
|
1907
|
+
glClearColor(self.backgroundColor[0], self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3])
|
|
1908
|
+
glMatrixMode(GL_PROJECTION)
|
|
1909
|
+
glLoadMatrixf(self.projection)
|
|
1910
|
+
glMatrixMode(GL_MODELVIEW)
|
|
1911
|
+
|
|
1912
|
+
# Retrieve transposed float32 coefficients.
|
|
1913
|
+
coefs = spline.cache["coefs32"]
|
|
1914
|
+
|
|
1915
|
+
# Contract spline if it's animating.
|
|
1916
|
+
nInd = spline.metadata["animate"]
|
|
1917
|
+
if nInd is not None:
|
|
1918
|
+
# Contraction value is set to cycle every 10 seconds (10000 ms).
|
|
1919
|
+
u1 = spline.knots[nInd][spline.order[nInd] - 1]
|
|
1920
|
+
u2 = spline.knots[nInd][spline.nCoef[nInd]]
|
|
1921
|
+
u = u1 + 0.49999 * (u2 - u1) * (1.0 - np.cos(2.0 * np.pi * self.frameCount * self.MsPerFrame / 10000))
|
|
1922
|
+
# Contract spline.
|
|
1923
|
+
knots = spline.cache["knots32"]
|
|
1924
|
+
ix, bValues = spline.bspline_values(None, knots[nInd], spline.order[nInd], u)
|
|
1925
|
+
coefs = np.moveaxis(coefs, spline.nInd - nInd - 1, -1) # Account for transpose
|
|
1926
|
+
coefs = coefs[..., ix - spline.order[nInd]:ix] @ bValues
|
|
1927
|
+
knots = [knots[i] for i in range(spline.nInd) if i != nInd]
|
|
1928
|
+
spline = type(spline)(spline.nInd - 1, coefs.shape[-1],
|
|
1929
|
+
[spline.order[i] for i in range(spline.nInd) if i != nInd],
|
|
1930
|
+
[spline.nCoef[i] for i in range(spline.nInd) if i != nInd],
|
|
1931
|
+
knots, coefs.T, spline.metadata)
|
|
1932
|
+
spline.cache = {"knots32": knots, "coefs32": coefs}
|
|
1933
|
+
|
|
1934
|
+
# Transform coefs by view transform.
|
|
1935
|
+
drawCoefficients = np.empty(coefs.shape, np.float32)
|
|
1936
|
+
drawCoefficients[..., :3] = coefs[..., :3] @ transform[:3,:3] + transform[3,:3]
|
|
1937
|
+
drawCoefficients[..., 3:] = coefs[..., 3:]
|
|
1938
|
+
|
|
1939
|
+
# Draw spline.
|
|
1940
|
+
if spline.nInd == 0 or spline.order[0] == 1:
|
|
1941
|
+
self._DrawPoints(spline, drawCoefficients)
|
|
1942
|
+
elif spline.nInd == 1:
|
|
1943
|
+
self._DrawCurve(spline, drawCoefficients)
|
|
1944
|
+
elif spline.nInd == 2:
|
|
1945
|
+
self._DrawSurface(spline, drawCoefficients)
|
|
1946
|
+
elif spline.nInd == 3:
|
|
1947
|
+
self._DrawSolid(spline, drawCoefficients)
|
|
1948
|
+
|
|
1293
1949
|
class CurveProgram:
|
|
1294
1950
|
""" Compile curve program """
|
|
1295
1951
|
def __init__(self, frame):
|
|
@@ -1306,7 +1962,7 @@ class CurveProgram:
|
|
|
1306
1962
|
curveTCShader,
|
|
1307
1963
|
shaders.compileShader(frame.curveTEShaderCode.format(
|
|
1308
1964
|
computeBSplineCode=frame.computeBSplineCode,
|
|
1309
|
-
maxOrder=
|
|
1965
|
+
maxOrder=frame.maxOrder), GL_TESS_EVALUATION_SHADER),
|
|
1310
1966
|
shaders.compileShader(frame.curveFragmentShaderCode, GL_FRAGMENT_SHADER))
|
|
1311
1967
|
else:
|
|
1312
1968
|
self.curveProgram = shaders.compileProgram(
|
|
@@ -1315,7 +1971,7 @@ class CurveProgram:
|
|
|
1315
1971
|
computeSampleRateCode=frame.computeSampleRateCode,
|
|
1316
1972
|
computeCurveSamplesCode=frame.computeCurveSamplesCode,
|
|
1317
1973
|
computeBSplineCode=frame.computeBSplineCode,
|
|
1318
|
-
maxOrder=
|
|
1974
|
+
maxOrder=frame.maxOrder), GL_GEOMETRY_SHADER),
|
|
1319
1975
|
shaders.compileShader(frame.curveFragmentShaderCode, GL_FRAGMENT_SHADER))
|
|
1320
1976
|
|
|
1321
1977
|
glUseProgram(self.curveProgram)
|
|
@@ -1338,8 +1994,12 @@ class CurveProgram:
|
|
|
1338
1994
|
|
|
1339
1995
|
class SurfaceProgram:
|
|
1340
1996
|
""" Compile surface program """
|
|
1341
|
-
def __init__(self, frame, nDep, splineColorDeclarations, initializeSplineColor, computeSplineColor, postProcessSplineColor):
|
|
1997
|
+
def __init__(self, frame, trimmed, nDep, splineColorDeclarations, initializeSplineColor, computeSplineColor, postProcessSplineColor):
|
|
1342
1998
|
if frame.tessellationEnabled:
|
|
1999
|
+
if trimmed:
|
|
2000
|
+
compiledFragmentShader = shaders.compileShader(frame.trimmedSurfaceFragmentShaderCode, GL_FRAGMENT_SHADER)
|
|
2001
|
+
else:
|
|
2002
|
+
compiledFragmentShader = shaders.compileShader(frame.surfaceFragmentShaderCode, GL_FRAGMENT_SHADER)
|
|
1343
2003
|
self.surfaceProgram = shaders.compileProgram(
|
|
1344
2004
|
shaders.compileShader(frame.surfaceVertexShaderCode, GL_VERTEX_SHADER),
|
|
1345
2005
|
shaders.compileShader(frame.surfaceTCShaderCode.format(
|
|
@@ -1353,8 +2013,8 @@ class SurfaceProgram:
|
|
|
1353
2013
|
initializeSplineColor=initializeSplineColor,
|
|
1354
2014
|
computeSplineColor=computeSplineColor,
|
|
1355
2015
|
postProcessSplineColor=postProcessSplineColor,
|
|
1356
|
-
maxOrder=
|
|
1357
|
-
|
|
2016
|
+
maxOrder=frame.maxOrder), GL_TESS_EVALUATION_SHADER),
|
|
2017
|
+
compiledFragmentShader,
|
|
1358
2018
|
validate = False)
|
|
1359
2019
|
else:
|
|
1360
2020
|
self.surfaceProgram = shaders.compileProgram(
|
|
@@ -1368,9 +2028,10 @@ class SurfaceProgram:
|
|
|
1368
2028
|
initializeSplineColor=initializeSplineColor,
|
|
1369
2029
|
computeSplineColor=computeSplineColor,
|
|
1370
2030
|
postProcessSplineColor=postProcessSplineColor,
|
|
1371
|
-
maxOrder=
|
|
2031
|
+
maxOrder=frame.maxOrder), GL_GEOMETRY_SHADER),
|
|
1372
2032
|
shaders.compileShader(frame.surfaceSimpleFragmentShaderCode, GL_FRAGMENT_SHADER))
|
|
1373
2033
|
|
|
2034
|
+
# Initialize program parameters.
|
|
1374
2035
|
glUseProgram(self.surfaceProgram)
|
|
1375
2036
|
self.aSurfaceParameters = glGetAttribLocation(self.surfaceProgram, "aParameters")
|
|
1376
2037
|
glBindBuffer(GL_ARRAY_BUFFER, frame.parameterBuffer)
|
|
@@ -1384,7 +2045,10 @@ class SurfaceProgram:
|
|
|
1384
2045
|
self.uSurfaceOptions = glGetUniformLocation(self.surfaceProgram, 'uOptions')
|
|
1385
2046
|
self.uSurfaceSplineData = glGetUniformLocation(self.surfaceProgram, 'uSplineData')
|
|
1386
2047
|
glUniform1i(self.uSurfaceSplineData, 0) # GL_TEXTURE0 is the spline buffer texture
|
|
1387
|
-
|
|
2048
|
+
if trimmed and frame.tessellationEnabled:
|
|
2049
|
+
self.uTrimTextureMap = glGetUniformLocation(self.surfaceProgram, 'uTrimTextureMap')
|
|
2050
|
+
glUniform1i(self.uTrimTextureMap, 1) # GL_TEXTURE1 is the trim texture map
|
|
2051
|
+
|
|
1388
2052
|
def ResetBounds(self, frame):
|
|
1389
2053
|
"""Reset bounds and other frame configuration for surface program"""
|
|
1390
2054
|
glUseProgram(self.surfaceProgram)
|