@@ -86,6 +86,11 @@ const COS_STRAIGHT_CORNER = Math.cos(5 * (Math.PI / 180));
8686// Angle per triangle for approximating round line joins.
8787const DEG_PER_TRIANGLE = 20 ;
8888
89+ // Multiplier for sharpCornerOffset to determine the threshold distance where
90+ // cap scaling begins for elevated bevel joins. Caps are scaled down linearly
91+ // when segment length is below this threshold to prevent overlap artifacts.
92+ const CAP_SCALE_THRESHOLD_FACTOR = 4.0 ;
93+
8994type LineClips = {
9095 start : number ;
9196 end : number ;
@@ -130,6 +135,7 @@ class LineBucket implements Bucket {
130135 e2 : number ;
131136
132137 patternJoinNone : boolean ;
138+ currentLineJoinType : string ;
133139 segmentStart : number ;
134140 segmentStartf32 : number ;
135141 segmentPoints : Array < number > ;
@@ -630,6 +636,7 @@ class LineBucket implements Bucket {
630636
631637 const joinNone = join === 'none' ;
632638 this . patternJoinNone = this . hasPattern && joinNone ;
639+ this . currentLineJoinType = join ;
633640 this . segmentStart = 0 ;
634641 this . segmentStartf32 = 0 ;
635642 this . segmentPoints = [ ] ;
@@ -906,12 +913,28 @@ class LineBucket implements Bucket {
906913 this . addCurrentVertex ( currentVertex , joinNormal . mult ( - 1 ) , 0 , 0 , segment , lineProgressFeatures ) ;
907914
908915 } else if ( currentJoin === 'bevel' || currentJoin === 'fakeround' ) {
916+ const dist = currentVertex . dist ( prevVertex ) ;
917+
918+ // Special handling for elevated bevel joins only
919+ const isElevatedBevel = this . elevationType === 'offset' &&
920+ currentJoin === 'bevel' &&
921+ this . currentLineJoinType !== 'round' &&
922+ ! this . patternJoinNone ;
923+
924+ // For elevated bevel joins at close corners, scale down rotation to prevent overlap
925+ let capScale = 1.0 ;
926+ if ( isElevatedBevel && lineProgressFeatures != null && prevVertex && nextVertex ) {
927+ const widthThreshold = CAP_SCALE_THRESHOLD_FACTOR * sharpCornerOffset ;
928+ if ( dist < widthThreshold ) {
929+ capScale = Math . max ( 0.0 , dist / widthThreshold ) ;
930+ }
931+ }
932+
909933 if ( lineProgressFeatures != null && prevVertex ) {
910934 // Close previous segment with butt
911- this . addCurrentVertex ( currentVertex , endNormal ? endNormal : prevNormal , - 1 , - 1 , segment , lineProgressFeatures ) ;
935+ this . addCurrentVertex ( currentVertex , endNormal ? endNormal : prevNormal , - capScale , - capScale , segment , lineProgressFeatures ) ;
912936 }
913937
914- const dist = currentVertex . dist ( prevVertex ) ;
915938 const skipStraightEdges = dist <= 2 * sharpCornerOffset && currentJoin !== 'bevel' ;
916939 const join = joinNormal . mult ( lineTurnsLeft ? 1.0 : - 1.0 ) ;
917940 join . _mult ( miterLength ) ;
@@ -961,7 +984,7 @@ class LineBucket implements Bucket {
961984
962985 if ( lineProgressFeatures != null && nextVertex ) {
963986 // Start next segment with a butt
964- this . addCurrentVertex ( currentVertex , startNormal ? startNormal : nextNormal , 1 , 1 , segment , lineProgressFeatures ) ;
987+ this . addCurrentVertex ( currentVertex , startNormal ? startNormal : nextNormal , capScale , capScale , segment , lineProgressFeatures ) ;
965988 }
966989 } else if ( currentJoin === 'butt' ) {
967990 this . addCurrentVertex ( currentVertex , joinNormal , 0 , 0 , segment , lineProgressFeatures ) ; // butt cap
@@ -1022,6 +1045,21 @@ class LineBucket implements Bucket {
10221045 const stepY = ( to . y - from . y ) / steps ;
10231046 const stepZ = ( to . z - from . z ) / steps ;
10241047 const stepW = ( to . w - from . w ) / steps ;
1048+
1049+ // Calculate perpendicular normal directly from segment direction for interior vertices
1050+ const dx = to . x - from . x ;
1051+ const dy = to . y - from . y ;
1052+ const len = Math . sqrt ( dx * dx + dy * dy ) ;
1053+ // Degenerate segment, skip tessellation
1054+ if ( len === 0 ) return ;
1055+
1056+ const perpX = - dy / len ;
1057+ const perpY = dx / len ;
1058+ const interiorLeftX = perpX ;
1059+ const interiorLeftY = perpY ;
1060+ const interiorRightX = - perpX ;
1061+ const interiorRightY = - perpY ;
1062+
10251063 for ( let i = 1 ; i < steps ; ++ i ) {
10261064 from . x += stepX ;
10271065 from . y += stepY ;
@@ -1030,13 +1068,15 @@ class LineBucket implements Bucket {
10301068 stepsDistance += stepW ;
10311069 const lpf = this . evaluateLineProgressFeatures ( this . prevDistance + stepsDistance ) ;
10321070 this . scaledDistance = ( this . prevDistance + stepsDistance ) / this . totalDistance ;
1033- this . addHalfVertex ( from , leftX , leftY , round , false , endLeft , segment , lpf ) ;
1034- this . addHalfVertex ( from , rightX , rightY , round , true , - endRight , segment , lpf ) ;
1071+ // Use perpendicular extrusion for interior vertices
1072+ this . addHalfVertex ( from , interiorLeftX , interiorLeftY , round , false , 0 , segment , lpf ) ;
1073+ this . addHalfVertex ( from , interiorRightX , interiorRightY , round , true , 0 , segment , lpf ) ;
10351074 }
10361075 }
10371076 this . lineSoFar = to . w ;
10381077 this . scaledDistance = scaledDistance ;
10391078 const lpf = this . evaluateLineProgressFeatures ( this . distance ) ;
1079+
10401080 this . addHalfVertex ( to , leftX , leftY , round , false , endLeft , segment , lpf ) ;
10411081 this . addHalfVertex ( to , rightX , rightY , round , true , - endRight , segment , lpf ) ;
10421082 }
@@ -1097,8 +1137,9 @@ class LineBucket implements Bucket {
10971137
10981138 if ( lineProgressFeatures != null ) {
10991139 const dropOutOfBounds = this . elevationType === 'offset' ;
1100- const boundsMin = - 10 ;
1101- const boundsMax = EXTENT + 10 ;
1140+ const clipMargin = this . elevationType === 'offset' ? 2 : 10 ;
1141+ const boundsMin = - clipMargin ;
1142+ const boundsMax = EXTENT + clipMargin ;
11021143 const zOffset = lineProgressFeatures . zOffset ;
11031144 const vertex = new Point4D ( p . x , p . y , zOffset , this . lineSoFar ) ;
11041145 // tesellated chunks outside tile borders are not added.
0 commit comments