@@ -278,8 +278,6 @@ def isInitialized(self):
278278
279279# line ########################################################################
280280
281- SOLID , DASHED , DASHDOT , DOTTED = "-" , "--" , "-." , ":"
282-
283281
284282class GLLines2D (object ):
285283 """Object rendering curve as a polyline
@@ -288,17 +286,16 @@ class GLLines2D(object):
288286 :param yVboData: Y coordinates VBO
289287 :param colorVboData: VBO of colors
290288 :param distVboData: VBO of distance along the polyline
291- :param str style: Line style in: '-', '--', '-.', ':'
292289 :param List[float] color: RGBA color as 4 float in [0, 1]
293290 :param float width: Line width
294- :param float dashPeriod: Period of dashes
291+ :param List[float] dashPattern:
292+ "unscaled" dash pattern as 4 lengths in points (dash1, gap1, dash2, gap2).
293+ This pattern is scaled with the line width.
294+ Set to () to draw solid lines (default), and to None to disable rendering.
295295 :param drawMode: OpenGL drawing mode
296296 :param List[float] offset: Translation of coordinates (ox, oy)
297297 """
298298
299- STYLES = SOLID , DASHED , DASHDOT , DOTTED
300- """Supported line styles"""
301-
302299 _SOLID_PROGRAM = Program (
303300 vertexShader = """
304301 #version 120
@@ -383,11 +380,10 @@ def __init__(
383380 yVboData = None ,
384381 colorVboData = None ,
385382 distVboData = None ,
386- style = SOLID ,
387383 color = (0.0 , 0.0 , 0.0 , 1.0 ),
388384 gapColor = None ,
389385 width = 1 ,
390- dashPeriod = 10.0 ,
386+ dashPattern = () ,
391387 drawMode = None ,
392388 offset = (0.0 , 0.0 ),
393389 ):
@@ -419,26 +415,11 @@ def __init__(
419415 self .color = color
420416 self .gapColor = gapColor
421417 self .width = width
422- self ._style = None
423- self .style = style
424- self .dashPeriod = dashPeriod
418+ self .dashPattern = dashPattern
425419 self .offset = offset
426420
427421 self ._drawMode = drawMode if drawMode is not None else gl .GL_LINE_STRIP
428422
429- @property
430- def style (self ):
431- """Line style (Union[str,None])"""
432- return self ._style
433-
434- @style .setter
435- def style (self , style ):
436- if style in _MPL_NONES :
437- self ._style = None
438- else :
439- assert style in self .STYLES
440- self ._style = style
441-
442423 @classmethod
443424 def init (cls ):
444425 """OpenGL context initialization"""
@@ -449,39 +430,23 @@ def render(self, context):
449430
450431 :param RenderContext context:
451432 """
452- width = self .width / 72.0 * context .dpi
453-
454- style = self .style
455- if style is None :
433+ if self .dashPattern is None : # Nothing to display
456434 return
457435
458- elif style == SOLID :
436+ if self . dashPattern == (): # No dash: solid line
459437 program = self ._SOLID_PROGRAM
460438 program .use ()
461439
462- else : # DASHED, DASHDOT, DOTTED
440+ else : # Dashed line defined by 4 control points
463441 program = self ._DASH_PROGRAM
464442 program .use ()
465443
466- dashPeriod = self .dashPeriod * width
467- if self .style == DOTTED :
468- dash = (
469- 0.2 * dashPeriod ,
470- 0.5 * dashPeriod ,
471- 0.7 * dashPeriod ,
472- dashPeriod ,
473- )
474- elif self .style == DASHDOT :
475- dash = (
476- 0.3 * dashPeriod ,
477- 0.5 * dashPeriod ,
478- 0.6 * dashPeriod ,
479- dashPeriod ,
480- )
481- else :
482- dash = (0.5 * dashPeriod , dashPeriod , dashPeriod , dashPeriod )
483-
484- gl .glUniform4f (program .uniforms ["dash" ], * dash )
444+ # Scale pattern by width, convert from lengths in points to offsets in pixels
445+ scale = self .width / 72.0 * context .dpi
446+ dashOffsets = tuple (
447+ offset * scale for offset in numpy .cumsum (self .dashPattern )
448+ )
449+ gl .glUniform4f (program .uniforms ["dash" ], * dashOffsets )
485450
486451 if self .gapColor is None :
487452 # Use fully transparent color which gets discarded in shader
@@ -540,7 +505,7 @@ def render(self, context):
540505 yPosAttrib , 1 , gl .GL_FLOAT , False , 0 , self .yVboData
541506 )
542507
543- gl .glLineWidth (width )
508+ gl .glLineWidth (self . width / 72.0 * context . dpi )
544509 gl .glDrawArrays (self ._drawMode , 0 , self .xVboData .size )
545510
546511 gl .glDisable (gl .GL_LINE_SMOOTH )
@@ -1220,11 +1185,10 @@ def __init__(
12201185 colorData = None ,
12211186 xError = None ,
12221187 yError = None ,
1223- lineStyle = SOLID ,
12241188 lineColor = (0.0 , 0.0 , 0.0 , 1.0 ),
12251189 lineGapColor = None ,
12261190 lineWidth = 1 ,
1227- lineDashPeriod = 20 ,
1191+ lineDashPattern = () ,
12281192 marker = SQUARE ,
12291193 markerColor = (0.0 , 0.0 , 0.0 , 1.0 ),
12301194 markerSize = 7 ,
@@ -1311,11 +1275,10 @@ def deduce_baseline(baseline):
13111275 )
13121276
13131277 self .lines = GLLines2D ()
1314- self .lines .style = lineStyle
13151278 self .lines .color = lineColor
13161279 self .lines .gapColor = lineGapColor
13171280 self .lines .width = lineWidth
1318- self .lines .dashPeriod = lineDashPeriod
1281+ self .lines .dashPattern = lineDashPattern
13191282 self .lines .offset = self .offset
13201283
13211284 self .points = Points2D ()
@@ -1336,15 +1299,13 @@ def deduce_baseline(baseline):
13361299
13371300 distVboData = _proxyProperty (("lines" , "distVboData" ))
13381301
1339- lineStyle = _proxyProperty (("lines" , "style" ))
1340-
13411302 lineColor = _proxyProperty (("lines" , "color" ))
13421303
13431304 lineGapColor = _proxyProperty (("lines" , "gapColor" ))
13441305
13451306 lineWidth = _proxyProperty (("lines" , "width" ))
13461307
1347- lineDashPeriod = _proxyProperty (("lines" , "dashPeriod " ))
1308+ lineDashPattern = _proxyProperty (("lines" , "dashPattern " ))
13481309
13491310 marker = _proxyProperty (("points" , "marker" ))
13501311
@@ -1362,7 +1323,7 @@ def prepare(self):
13621323 """Rendering preparation: build indices and bounding box vertices"""
13631324 if self .xVboData is None :
13641325 xAttrib , yAttrib , cAttrib , dAttrib = None , None , None , None
1365- if self .lineStyle in ( DASHED , DASHDOT , DOTTED ) :
1326+ if self .lineDashPattern :
13661327 dists = distancesFromArrays (self .xData , self .yData , self ._ratio )
13671328 if self .colorData is None :
13681329 xAttrib , yAttrib , dAttrib = vertexBuffer (
@@ -1393,7 +1354,7 @@ def render(self, context):
13931354
13941355 :param RenderContext context: Rendering information
13951356 """
1396- if self .lineStyle in ( DASHED , DASHDOT , DOTTED ) :
1357+ if self .lineDashPattern :
13971358 visibleRanges = context .plotFrame .transformedDataRanges
13981359 xLimits = visibleRanges .x
13991360 yLimits = visibleRanges .y if self .yaxis == "left" else visibleRanges .y2
@@ -1450,7 +1411,7 @@ def pick(self, xPickMin, yPickMin, xPickMax, yPickMax):
14501411 :rtype: Union[List[int],None]
14511412 """
14521413 if (
1453- (self .marker is None and self .lineStyle is None )
1414+ (self .marker is None and self .lineDashPattern is None )
14541415 or self .xMin > xPickMax
14551416 or xPickMin > self .xMax
14561417 or self .yMin > yPickMax
@@ -1464,7 +1425,7 @@ def pick(self, xPickMin, yPickMin, xPickMax, yPickMax):
14641425 yPickMin = yPickMin - self .offset [1 ]
14651426 yPickMax = yPickMax - self .offset [1 ]
14661427
1467- if self .lineStyle is not None :
1428+ if self .lineDashPattern is not None :
14681429 # Using Cohen-Sutherland algorithm for line clipping
14691430 with numpy .errstate (invalid = "ignore" ): # Ignore NaN comparison warnings
14701431 codes = (
0 commit comments