Skip to content

Commit

Permalink
✈️
Browse files Browse the repository at this point in the history
  • Loading branch information
rreusser committed Aug 6, 2023
1 parent 497662c commit 605c7c0
Show file tree
Hide file tree
Showing 9 changed files with 495 additions and 45 deletions.
2 changes: 1 addition & 1 deletion karman-trefftz-airfoil/bundle.js

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions karman-trefftz-airfoil/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<!DOCTYPE html><html lang="en" dir="ltr"><head><title>Karman-Trefftz Airfoil</title><meta charset="utf-8"><meta name="twitter:image" content="http://rreusser.github.io/sketches/images/karman-trefftz-airfoil-thumbnail.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="application-name" content="Karman-Trefftz Airfoil">
<!DOCTYPE html><html lang="en" dir="ltr"><head><title>Karman-Trefftz Airfoil</title><meta charset="utf-8"><meta name="application-name" content="Karman-Trefftz Airfoil">
<meta name="subject" content="Flow over an airfoil, computed with the Karman-Trefftz conformal map and visualized on the GPU">
<meta name="abstract" content="Flow over an airfoil, computed with the Karman-Trefftz conformal map and visualized on the GPU">
<meta name="twitter:title" content="Karman-Trefftz Airfoil">
<meta name="description" content="Flow over an airfoil, computed with the Karman-Trefftz conformal map and visualized on the GPU">
<meta name="twitter:description" content="Flow over an airfoil, computed with the Karman-Trefftz conformal map and visualized on the GPU">
<meta name="author" content="Ricky Reusser">
<meta name="twitter:creator" content="Ricky Reusser">
<meta property="og:image" content="http://rreusser.github.io/sketches/images/karman-trefftz-airfoil-thumbnail.jpg">
<meta name="twitter:card" content="summary">
<meta property="og:title" content="Karman-Trefftz Airfoil">
<meta property="og:description" content="Flow over an airfoil, computed with the Karman-Trefftz conformal map and visualized on the GPU">
<meta property="article:author" content="Ricky Reusser">
Expand Down
122 changes: 122 additions & 0 deletions src/src/karman-trefftz-airfoil/draw-arrow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use strict';

module.exports = function (regl) {
return regl({
vert: `
precision highp float;
uniform mat4 modelview;
uniform float uTailWidth, uAspect, uScale, uForeshortening, uInset;
uniform vec2 uArrowheadShape;
attribute vec2 aVertex, aNormal;
attribute vec4 aArrow;
void main () {
vec4 vp = vec4(aVertex, 0, 1);
vec4 vpn = vec4(aVertex + uScale * aNormal, 0, 1);
// Project the vertex into homogeneous coordinates
vec4 p = modelview * vp;
// Project the vertex + normal into homogeneous coordinates
vec4 pn = modelview * vpn;
float foreshortening = max(0.1, sqrt(1.0 - uForeshortening * abs((vp - vpn).z) / length((vp - vpn).xyz)));
// Use the y component of aArrow to select either p or pn
gl_Position = mix(p, pn, aArrow.y);
// Compute a screen-space vector parallel to the arrow.
// This step includes "perspective division" to convert homogeneous
// 4D coordinates into screen space coordinates.
// NB: it also includes an aspect ratio to scale x and y equally. This could be
// done more cleanly.
vec2 unitVector = normalize((pn.xy / pn.w - p.xy / p.w) * vec2(uAspect, 1));
// Rotate 90 degrees to get a perpendicular vector
vec2 perpUnitVector = vec2(-unitVector.y, unitVector.x);
// Perturb the point according to the aArrow instance data
gl_Position.xy += (
// Offset perpendicular to the length of the arrow:
perpUnitVector * (aArrow.x * (uTailWidth - uInset) + aArrow.w * (uArrowheadShape.y - 1.5 * uInset)) +
// and parallel to the length of the arrow:
+ unitVector * (aArrow.z * (uArrowheadShape.x - 3.5 * uInset) - 2.5 * uInset * aArrow.y) * foreshortening
// This final step is just a bit tricky, but we need to pull the aspect
// ratio back out and then multiply by w to get the arrow scaled correctly
) / vec2(uAspect, 1) * gl_Position.w;
}
`,
frag: `
precision highp float;
uniform vec4 uColor;
void main () {
gl_FragColor = uColor;
}
`,
attributes: {
aVertex: {
buffer: regl.prop('vertices'),
divisor: 1, // Advance the mesh vertex once per instance
stride: 8 // each instance advances 3 floats (= 12 bytes)
},
aNormal: {
buffer: regl.prop('normals'),
divisor: 1,
stride: 8
},
// prettier-ignore
aArrow: new Float32Array([
// The per-instance triangles are defined in terms of four pieces of data which tell where
// on the arrow we are, using the mesh vertex and mesh normal as inputs. The components are:
// x: selects the position perpendicular to the length of the arrow in screen space
// y: selects either the (vertex) or (vertex + normal) in 3D space
// z: selects the arrowhead length-wise offset in screen space
// w: selects the arrowhead width-wise offset in screen space
// The first triangle of the tail:
-1, 0, 0, 0,
1, 0, 0, 0,
1, 1, -1, 0,

// The second triangle of the tail:
-1, 0, 0, 0,
1, 1, -1, 0,
-1, 1,-1, 0,

// The arrowhead:
0, 1, -1, -1,
0, 1, -1, 1,
0, 1, 0, 0
])
},
uniforms: {
// Define the screen-space line width in terms of a property but also
// scaled by the pixel ratio so that it remains constant at different
// pixel ratios. (The framebuffer height is just for proper screen-space
// scaling)
uTailWidth: (ctx, props) =>
(props.arrowTailWidth / ctx.framebufferHeight) * ctx.pixelRatio,
// Define the shape of the arrowhead. This just the scale factor
// for the ones and zeros above.
uArrowheadShape: (ctx, props) => [
(props.arrowheadLength / ctx.framebufferHeight) * ctx.pixelRatio * 2.0,
(props.arrowheadWidth / ctx.framebufferHeight) * ctx.pixelRatio
],
uInset: (ctx, props) => (props.inset || 0) / ctx.framebufferWidth * ctx.pixelRatio,

// The aspect ratio affects computation of offsets for the screen-space
// lines.
uAspect: ctx => ctx.framebufferWidth / ctx.framebufferHeight,
uColor: regl.prop('arrowColor'),
uForeshortening: (ctx, props) => (props.foreshortening ? 1 : 0),

// Not really necessary, but an overall scale factor for the normals
uScale: regl.prop('arrowScale')
},
primitive: 'triangles',
instances: (ctx, props) => props.vertexCount, // One instance per vertex
count: 9 // Nine vertices per instance
});
};
120 changes: 98 additions & 22 deletions src/src/karman-trefftz-airfoil/draw-mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const glsl = require('glslify');

module.exports = function (regl, mesh) {
module.exports = function (regl, mesh, colorscale) {
return regl({
vert: `
precision highp float;
Expand All @@ -15,7 +15,21 @@ module.exports = function (regl, mesh) {
#define OPI2 0.15915494309
vec2 cdiv (vec2 a, vec2 b) {
return vec2(a.x * b.x + a.y * b.y, a.y * b.x - a.x * b.y) / dot(b, b);
float e, f;
float g = 1.0;
float h = 1.0;
if( abs(b.x) >= abs(b.y) ) {
e = b.y / b.x;
f = b.x + b.y * e;
h = e;
} else {
e = b.x / b.y;
f = b.x * e + b.y;
g = e;
}
return (a * g + h * vec2(a.y, -a.x)) / f;
}
vec2 cmul (vec2 a, vec2 b) {
Expand All @@ -26,8 +40,21 @@ module.exports = function (regl, mesh) {
return vec2(a.x * a.x - a.y * a.y, 2.0 * a.x * a.y);
}
vec2 cinv (vec2 a) {
return vec2(a.x, -a.y) / dot(a, a);
vec2 cinv (vec2 b) {
float e, f;
vec2 g = vec2(1, -1);
if( abs(b.x) >= abs(b.y) ) {
e = b.y / b.x;
f = b.x + b.y * e;
g.y = -e;
} else {
e = b.x / b.y;
f = b.x * e + b.y;
g.x = e;
}
return g / f;
}
float cmag2 (vec2 a) {
Expand Down Expand Up @@ -78,46 +105,95 @@ module.exports = function (regl, mesh) {
// Compute the jacobian:
vec2 dzdzeta = 4.0 * n * n * cdiv(cmul(opzn, omzn), cmul(csqr(r0 * zeta + mu) - vec2(1, 0), csqr(opzn - omzn)));
//vec2 dzdzeta = vec2(1, 0);
cp = 1.0 - cmag2(cdiv(wt, dzdzeta)) * colorScale;
// Compute z^2 - 1
psi = (r - 1.0 / r) * sin(theta + alpha) + circulation * OPI2 * log(r);
//z.x -= n;
//z /= scale;
//z.x += 0.5;
//z *= 4.0;
gl_Position = modelview * vec4(z, 0, 1);
}
`,
frag: glsl(`
#extension GL_OES_standard_derivatives : enable
precision highp float;
#pragma glslify: colormap = require(glsl-colormap/viridis)
varying float psi, cp, rgrid;
varying vec2 uv, xy;
uniform float cpAlpha, streamAlpha, gridAlpha;
uniform vec2 mu;
uniform sampler2D colorscale;
#define PI 3.14159265358979
#pragma glslify: grid = require(glsl-solid-wireframe/cartesian/scaled)
float hypot (vec2 z) {
float x = abs(z.x);
float y = abs(z.y);
float t = min(x, y);
x = max(x, y);
t = t / x;
return x * sqrt(1.0 + t * t);
//return (z.x == 0.0 && z.y == 0.0) ? 0.0 : x * sqrt(1.0 + t * t);
}
float linearstep(float edge0, float edge1, float x) {
return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
}
const float octaveDivisions = 8.0;
const int octaves = 4;
const float n = float(octaves);
float blendedContours (float f, float minSpacing, float width, float antialiasing) {
float screenSpaceLogGrad = hypot(vec2(dFdx(f), dFdy(f)));
float localOctave = log2(screenSpaceLogGrad * minSpacing) / log2(octaveDivisions);
float contourSpacing = pow(octaveDivisions, ceil(localOctave));
float plotVar = f / contourSpacing;
float widthScale = contourSpacing / screenSpaceLogGrad;
float contourSum = 0.0;
for(int i = 0; i < octaves; i++) {
float t = float(i + 1) - fract(localOctave);
float weight = smoothstep(0.0, 1.0, t) * smoothstep(n, n - 1.0, t) * t;
contourSum += weight * linearstep(
0.5 * (width + antialiasing),
0.5 * (width - antialiasing),
(0.5 - abs(fract(plotVar) - 0.5)) * widthScale
);
// Rescale for the next octave
widthScale *= octaveDivisions;
plotVar /= octaveDivisions;
}
return contourSum / n;
}
const float feather = 1.0;
const float streamWidth = 0.75;
const float pressureWidth = 0.75;
const float streamWidth = 1.5;
const float pressureWidth = 1.5;
const float boundaryWidth = 3.0;
void main () {
float boundary = grid(rgrid, boundaryWidth, feather);
float pressure = 1.0 - (1.0 - grid(cp * 20.0, pressureWidth, feather)) * cpAlpha;
float stream = ((1.0 - grid(1.5 * psi, streamWidth, feather)) + 0.4 * (1.0 - grid(15.0 * psi, streamWidth, feather))) * streamAlpha;
vec3 color = colormap(max(0.0, min(1.0, cp))).xyz;
float gridLines = ((1.0 - grid(xy, 0.75, feather)) + 0.4 * (1.0 - grid(xy * 10.0, 0.75, feather))) * gridAlpha;
color *= 1.0 - gridLines;
gl_FragColor = vec4((color * pressure + stream) * boundary, 1);
void main () {
float boundary = 1.0 - grid(rgrid * 0.5, boundaryWidth, feather);
float pressure = (blendedContours(cp, 2.0, pressureWidth, feather)) * cpAlpha;
float stream = blendedContours(psi, 1.0, streamWidth, feather) * streamAlpha;
vec3 color = texture2D(colorscale, vec2(
1.0 - pow(atan(8.0 * (1.0 - cp)) / (0.5 * PI), 2.0),
0.5
)).xyz;
float gridLines = max(
blendedContours(xy.x, 2.0, 1.25, feather),
blendedContours(xy.y, 2.0, 1.25, feather)
) * gridAlpha;
color = mix(color, vec3(1), max(max(max(gridLines, pressure), stream), boundary));
gl_FragColor = vec4(color, 1);//vec4((color * pressure + stream) * boundary, 1);
}
`),
uniforms: {
colorscale
},
attributes: {
rth: mesh.positions,
//barycentric: mesh.barycentric,
Expand Down
Loading

0 comments on commit 605c7c0

Please sign in to comment.