I'm new to Swift, learning SwiftUI. I am writing a finite element analysis code, and at the point of trying to present the results in a "fringe plot" that fills each element with colors corresponding to strains or stresses. It's currently 2D; will be 3D in the next iteration.
I am trying to figure out how to proceed for results display: ScenetKit, Metal, etc.
There is an excellent example with the Metal documentation: Using a Render Pipeline to Render Primitives, that uses a 2D triangle, with colors at each corner, to interpolate colors in the area that would serve my immediate 2D needs.
However, this is not SwiftUI, so very challenging for me to try to implement with my code. (I don't understand the majority of this...) Is there an easy way, maybe just Core Graphics, to do color interpolation over a triangle via SwiftUI itself? (The linear gradient method seemed promising, but I don't know how to interpolate with 3 inputs for the vertices of a triangle.)
Or is there a way to use the existing example in Metal by translating into SwiftUI? Or is there a better choice? Thanks, in advance, for your suggestions!
I am trying to figure out how to proceed for results display: ScenetKit, Metal, etc.
There is an excellent example with the Metal documentation: Using a Render Pipeline to Render Primitives, that uses a 2D triangle, with colors at each corner, to interpolate colors in the area that would serve my immediate 2D needs.
However, this is not SwiftUI, so very challenging for me to try to implement with my code. (I don't understand the majority of this...) Is there an easy way, maybe just Core Graphics, to do color interpolation over a triangle via SwiftUI itself? (The linear gradient method seemed promising, but I don't know how to interpolate with 3 inputs for the vertices of a triangle.)
Or is there a way to use the existing example in Metal by translating into SwiftUI? Or is there a better choice? Thanks, in advance, for your suggestions!
In trying to answer my own question, I have developed a mostly satisfactory solution using the LinearGradient with SwiftUI and a ZStack. It's not quite the quality of the solution that the Metal documentation: "Using a Render Pipeline to Render Primitives" (my solution is too bright in the center of the triangle) and I don't know how this might scale over hundreds of thousands of triangles, but it gets me started. Thanks to those who looked into my issue.
Just in case someone is interested, here's my code:
import SwiftUI
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 150.0, y: 0.0))
path.addLine(to: CGPoint(x: 0.0, y: 300.0))
path.addLine(to: CGPoint(x: 300.0, y: 300.0))
path.addLine(to: CGPoint(x: 150.0, y: 0.0))
return path
}
}
struct ContentView: View {
var body: some View {
let colorsTop = Gradient(colors: [Color(red: 0, green: 0, blue: 1), .white])
let colorsLeading = Gradient(colors: [Color(red: 0, green: 1, blue: 0), .white])
let colorsTrailing = Gradient(colors:[Color(red: 1, green: 0, blue: 0), .white])
let gradTop = (LinearGradient(gradient: colorsTop, startPoint: UnitPoint(x: 0.5, y: 0.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
let gradLeading = (LinearGradient(gradient: colorsLeading, startPoint: UnitPoint(x: 0.0, y: 1.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
let gradTrailing = (LinearGradient(gradient: colorsTrailing, startPoint: UnitPoint(x: 1.0, y: 1.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
VStack {
// If the background is white, the blendMode is multiply
ZStack {
Triangle()
.fill(gradTop)
.frame(width: 300, height: 300)
.blendMode(.multiply)
Triangle()
.fill(gradLeading)
.frame(width: 300, height: 300)
.blendMode(.multiply)
Triangle()
.fill(gradTrailing)
.frame(width: 300, height: 300)
.blendMode(.multiply)
}
}
// if the background is black, the blendMode is difference
ZStack {
Triangle()
.fill(gradTop)
.frame(width: 300, height: 300)
.blendMode(.difference)
Triangle()
.fill(gradLeading)
.frame(width: 300, height: 300)
.blendMode(.difference)
Triangle()
.fill(gradTrailing)
.frame(width: 300, height: 300)
.blendMode(.difference)
}
.background(Color.black)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Just in case someone is interested, here's my code:
import SwiftUI
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 150.0, y: 0.0))
path.addLine(to: CGPoint(x: 0.0, y: 300.0))
path.addLine(to: CGPoint(x: 300.0, y: 300.0))
path.addLine(to: CGPoint(x: 150.0, y: 0.0))
return path
}
}
struct ContentView: View {
var body: some View {
let colorsTop = Gradient(colors: [Color(red: 0, green: 0, blue: 1), .white])
let colorsLeading = Gradient(colors: [Color(red: 0, green: 1, blue: 0), .white])
let colorsTrailing = Gradient(colors:[Color(red: 1, green: 0, blue: 0), .white])
let gradTop = (LinearGradient(gradient: colorsTop, startPoint: UnitPoint(x: 0.5, y: 0.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
let gradLeading = (LinearGradient(gradient: colorsLeading, startPoint: UnitPoint(x: 0.0, y: 1.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
let gradTrailing = (LinearGradient(gradient: colorsTrailing, startPoint: UnitPoint(x: 1.0, y: 1.0), endPoint: UnitPoint(x: 0.5, y: 0.6)))
VStack {
// If the background is white, the blendMode is multiply
ZStack {
Triangle()
.fill(gradTop)
.frame(width: 300, height: 300)
.blendMode(.multiply)
Triangle()
.fill(gradLeading)
.frame(width: 300, height: 300)
.blendMode(.multiply)
Triangle()
.fill(gradTrailing)
.frame(width: 300, height: 300)
.blendMode(.multiply)
}
}
// if the background is black, the blendMode is difference
ZStack {
Triangle()
.fill(gradTop)
.frame(width: 300, height: 300)
.blendMode(.difference)
Triangle()
.fill(gradLeading)
.frame(width: 300, height: 300)
.blendMode(.difference)
Triangle()
.fill(gradTrailing)
.frame(width: 300, height: 300)
.blendMode(.difference)
}
.background(Color.black)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}