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/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 DrawableSpline
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 `DrawableSpline`.
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 = _set_color(r, g, b, a)
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 * DrawableSpline._maxFloats, None, GL_STATIC_READ)
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.surface4Program = SurfaceProgram(self, 4,
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.surface6Program = SurfaceProgram(self, 6, "", "splineColor = vec3(0.0, 0.0, 0.0);",
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 = defaultAnchorDistance + clipDistance
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=DrawableSpline.maxOrder), GL_TESS_EVALUATION_SHADER),
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=DrawableSpline.maxOrder), GL_GEOMETRY_SHADER),
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=DrawableSpline.maxOrder), GL_TESS_EVALUATION_SHADER),
1357
- shaders.compileShader(frame.surfaceFragmentShaderCode, GL_FRAGMENT_SHADER),
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=DrawableSpline.maxOrder), GL_GEOMETRY_SHADER),
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)