@@ -140,12 +140,13 @@ public final class Geometry: Hashable {
140140 debug: Bool = false
141141 ) {
142142 var material = material
143+ var useMaterialForCache = false
143144 var children = children
144145 var type = type
145146 switch type {
146147 case var . extrude( paths, options) :
147- ( paths, material) = paths. fixupColors ( material: material)
148- ( options. along, material) = options. along. fixupColors ( material: material)
148+ ( paths, material) = paths. vertexColorsToMaterial ( material: material)
149+ ( options. along, material) = options. along. vertexColorsToMaterial ( material: material)
149150 type = . extrude( paths, options)
150151 switch ( paths. count, options. along. count) {
151152 case ( 0 , 0 ) :
@@ -158,7 +159,9 @@ public final class Geometry: Hashable {
158159 assert ( children. isEmpty)
159160 type = . extrude( [ ] , . default)
160161 children = paths. map { path in
161- Geometry (
162+ var path = path
163+ ( path, material) = path. vertexColorsToMaterial ( material: material)
164+ return Geometry (
162165 type: . extrude( [ path] , options) ,
163166 name: nil ,
164167 transform: . identity,
@@ -170,7 +173,7 @@ public final class Geometry: Hashable {
170173 }
171174 }
172175 case . lathe( var paths, let segments) :
173- ( paths, material) = paths. fixupColors ( material: material)
176+ ( paths, material) = paths. vertexColorsToMaterial ( material: material)
174177 type = . lathe( paths, segments: segments)
175178 switch paths. count {
176179 case 0 :
@@ -196,7 +199,7 @@ public final class Geometry: Hashable {
196199 }
197200 case var . fill( paths) :
198201 // TODO: why didn't we apply the same logic as above for fill?
199- ( paths, material) = paths. fixupColors ( material: material)
202+ ( paths, material) = paths. vertexColorsToMaterial ( material: material)
200203 type = . fill( paths)
201204 switch paths. count {
202205 case 0 :
@@ -208,7 +211,9 @@ public final class Geometry: Hashable {
208211 assert ( children. isEmpty)
209212 case let . mesh( mesh) :
210213 material = mesh. polygons. first? . material as? Material ?? material
211- case . union, . xor, . difference, . intersection, . stencil, . hull, . minkowski:
214+ case . hull, . minkowski:
215+ useMaterialForCache = true
216+ case . union, . xor, . difference, . intersection, . stencil:
212217 material = children. first? . material ?? . default
213218 case . group:
214219 if debug {
@@ -225,9 +230,13 @@ public final class Geometry: Hashable {
225230 self . _sourceLocation = sourceLocation
226231 self . debug = debug
227232
233+ var hasVariedMaterials = false
228234 var isOpaque = material. isOpaque
229235 func flattenedCacheKey( for geometry: Geometry ) -> GeometryCache . Key {
230236 isOpaque = isOpaque && geometry. material. isOpaque
237+ if !hasVariedMaterials, geometry. material != material {
238+ hasVariedMaterials = true
239+ }
231240 return GeometryCache . Key (
232241 type: geometry. type,
233242 material: geometry. material == material ? nil : geometry. material,
@@ -238,18 +247,19 @@ public final class Geometry: Hashable {
238247 )
239248 }
240249
241- self . cacheKey = GeometryCache . Key (
250+ let childKeys = type. isLeafGeometry ? [ ] : children. map ( flattenedCacheKey)
251+
252+ // Must be set after child keys are generated
253+ self . isOpaque = isOpaque
254+ self . cacheKey = . init(
242255 type: type,
243- material: nil ,
256+ material: useMaterialForCache && hasVariedMaterials ? material : nil ,
244257 smoothing: smoothing,
245258 transform: . identity,
246259 flipped: transform. isFlipped,
247- children: type . isLeafGeometry ? [ ] : children . map ( flattenedCacheKey )
260+ children: childKeys
248261 )
249262
250- // Must be set after cache key is generated
251- self . isOpaque = isOpaque
252-
253263 // Compute the overestimated, non-transformed bounds
254264 switch type {
255265 case . difference, . stencil:
@@ -601,9 +611,10 @@ private extension Geometry {
601611 case let . loft( paths) :
602612 mesh = Mesh . loft ( paths) . makeWatertight ( )
603613 case let . hull( vertices) :
604- let m = Mesh . convexHull ( of: vertices, material: Material . default , isCancelled: isCancelled)
614+ let m = Mesh . convexHull ( of: vertices, material: material , isCancelled: isCancelled)
605615 let meshes = ( [ m] + childMeshes( callback) ) . map { $0. materialToVertexColors ( material: material) }
606- mesh = . convexHull( of: meshes, isCancelled: isCancelled) . fixupColors ( material: material)
616+ mesh = . convexHull( of: meshes, isCancelled: isCancelled)
617+ . vertexColorsToMaterial ( material: material) . replacing ( material, with: nil )
607618 case . minkowski:
608619 var children = ArraySlice ( children. enumerated ( ) . sorted {
609620 switch ( $0. 1 . type, $1. 1 . type) {
@@ -648,14 +659,16 @@ private extension Geometry {
648659 sum = first. flattened ( callback) . materialToVertexColors ( material: first. material)
649660 }
650661 while let next = children. popFirst ( ) {
651- if let path = next. path? . transformed ( by: next. transform) {
662+ if var path = next. path? . transformed ( by: next. transform) {
663+ path = path. materialToVertexColors ( material: next. material)
652664 sum = sum. minkowskiSum ( with: path, isCancelled: isCancelled)
653665 } else {
654666 let mesh = next. flattened ( callback) . materialToVertexColors ( material: next. material)
655667 sum = sum. minkowskiSum ( with: mesh, isCancelled: isCancelled)
656668 }
657669 }
658- mesh = sum. fixupColors ( material: material) . makeWatertight ( )
670+ mesh = sum. vertexColorsToMaterial ( material: material)
671+ . replacing ( material, with: nil ) . makeWatertight ( )
659672 case let . fill( paths) :
660673 mesh = Mesh . fill ( paths. map { $0. closed ( ) } , isCancelled: isCancelled) . makeWatertight ( )
661674 case . union, . lathe, . extrude:
@@ -756,70 +769,130 @@ private extension Geometry {
756769 }
757770}
758771
759- private extension Collection < Path > {
772+ private extension [ Path ] {
773+ /// Returns the uniform color of all vertices, or nil if they have different colors
774+ var uniformVertexColor : Color ? {
775+ let uniformColor = first? . uniformVertexColor ?? . white
776+ return allSatisfy { $0. uniformVertexColor == uniformColor } ? uniformColor : nil
777+ }
778+
760779 /// Convert uniform point colors to a material instead
761- func fixupColors ( material: Material ) -> ( [ Path ] , Material ) {
780+ func vertexColorsToMaterial ( material: Material ) -> ( [ Path ] , Material ) {
762781 guard material. texture == nil else {
763- return ( Array ( self ) , material)
764- }
765- var current : Color ?
766- for path in self {
767- for point in path. points {
768- if current == nil {
769- current = point. color
770- } else if point. color != current {
771- var material = material
772- material. albedo = . color( . white)
773- return ( Array ( self ) , material)
774- }
782+ return ( self , material)
783+ }
784+ if let uniformVertexColor {
785+ if uniformVertexColor == . white {
786+ return ( self , material)
775787 }
788+ var material = material
789+ material. albedo = . color( uniformVertexColor)
790+ return ( map { $0. withColor ( nil ) } , material)
776791 }
777792 var material = material
778- material. albedo = ( current ?? material. color) . map { . color( $0) }
779- return ( map { $0. withColor ( nil ) } , material)
793+ material. albedo = . color( . white)
794+ return ( self , material)
795+ }
796+ }
797+
798+ extension Path {
799+ /// Returns the uniform color of all vertices, or nil if they have different colors
800+ var uniformVertexColor : Color ? {
801+ let uniformColor = points. first? . color
802+ return points. allSatisfy { $0. color == uniformColor } ? ( uniformColor ?? . white) : nil
803+ }
804+
805+ /// Convert uniform point colors to a material instead
806+ func vertexColorsToMaterial( material: Material ) -> ( Path , Material ) {
807+ guard material. texture == nil else {
808+ return ( self , material)
809+ }
810+ if let uniformVertexColor {
811+ if uniformVertexColor == . white {
812+ return ( self , material)
813+ }
814+ var material = material
815+ material. albedo = . color( uniformVertexColor)
816+ return ( withColor ( nil ) , material)
817+ }
818+ var material = material
819+ material. albedo = . color( . white)
820+ return ( self , material)
821+ }
822+
823+ /// Convert material color to vertex colors
824+ func materialToVertexColors( material: ShapeScript . Material ? ) -> Path {
825+ guard let color = material? . color, color != . white, !hasColors else {
826+ return self
827+ }
828+ return withColor ( color)
780829 }
781830}
782831
783832extension Polygon {
833+ /// Returns the uniform color of all vertices, or nil if they have different colors
834+ var uniformVertexColor : Color ? {
835+ let uniformColor = vertices. first? . color ?? . white
836+ return vertices. allSatisfy { $0. color == uniformColor } ? uniformColor : nil
837+ }
838+
784839 /// Convert uniform vertex colors to a material instead
785- func fixupColors( material: ShapeScript . Material ) -> Polygon {
840+ func vertexColorsToMaterial( material: ShapeScript . Material ) -> Polygon {
841+ var material = self . material as? ShapeScript . Material ?? material
786842 guard material. texture == nil else {
787843 return withMaterial ( material)
788844 }
789- var current : Color ?
790- for point in vertices {
791- if current == nil {
792- current = point. color
793- } else if point. color != current {
794- var material = material
795- material. albedo = . color( . white)
845+ if let uniformVertexColor {
846+ if uniformVertexColor == . white {
796847 return withMaterial ( material)
797848 }
849+ var material = material
850+ material. albedo = . color( uniformVertexColor)
851+ return withoutVertexColors ( ) . withMaterial ( material)
798852 }
799- var material = material
800- material. albedo = ( current ?? material. color) . map { . color( $0) }
801- return mapVertexColors { _ in nil } . withMaterial ( material)
853+ material. albedo = . color( . white)
854+ return withMaterial ( material)
802855 }
803856
804- /// Convert material colors to a vertex colors
857+ /// Convert material color to vertex colors
805858 func materialToVertexColors( material: ShapeScript . Material ? ) -> Polygon {
806859 guard var material = self . material as? ShapeScript . Material ?? material,
807- let color = material. color
860+ let color = material. color, color != . white,
861+ !hasVertexColors
808862 else {
809863 return self
810864 }
811865 material. albedo = . color( . white)
812- return mapVertexColors { $0 * color } . withMaterial ( material)
866+ return mapVertexColors { _ in color } . withMaterial ( material)
813867 }
814868}
815869
816870extension Mesh {
871+ /// Returns the uniform color of all vertices, or nil if they have different colors
872+ var uniformVertexColor : Color ? {
873+ let uniformColor = polygons. first? . uniformVertexColor ?? . white
874+ return polygons. allSatisfy { $0. uniformVertexColor == uniformColor } ? uniformColor : nil
875+ }
876+
817877 /// Convert uniform vertex colors to a material instead
818- func fixupColors( material: ShapeScript . Material ) -> Mesh {
819- . init( polygons. map { $0. fixupColors ( material: material) } )
878+ func vertexColorsToMaterial( material: ShapeScript . Material ) -> Mesh {
879+ guard material. texture == nil else {
880+ return withMaterial ( material)
881+ }
882+ if let uniformVertexColor {
883+ if uniformVertexColor == . white {
884+ return withMaterial ( material)
885+ }
886+ var material = material
887+ material. albedo = . color( uniformVertexColor)
888+ return withoutVertexColors ( ) . withMaterial ( material)
889+ }
890+ var material = material
891+ material. albedo = . color( . white)
892+ return withMaterial ( material)
820893 }
821894
822- /// Convert material colors to a vertex colors
895+ /// Convert material colors to vertex colors
823896 func materialToVertexColors( material: ShapeScript . Material ? ) -> Mesh {
824897 . init( polygons. map { $0. materialToVertexColors ( material: material) } )
825898 }
0 commit comments