Quatrefoil
Render Three.js with Respo style code(experimental).
Demo(Chrome with a touch screen) http://r.tiye.me/Quatrefoil-GL/quatrefoil/
JS methods
some helpers are split in npm package:
yarn add @quamolit/quatrefoil-utils
Develop
Relies on https://calcit-lang.org .
yarn
caps # get calcit deps in `~/.config/calcit/modules/`
cr -1 js # compile js once
yarn vite
License
MIT
Touch Control
based on tool of https://github.com/Quatrefoil-GL/touch-control , which is designed for touch screens.
Shortcuts for viewport movememnt
Keyword shortcuts for viewport moving:
w
move forwards
move backwarda
move leftd
move left- "Up" move up
- "Down" move down
- "Shift Up" rotate to view up
- "Shift Down" rotate to view down
- "Left" rotate to view left
- "Right" rotate to view right
Materials
Material
, currently:line-basic
,:line-dashed
,:mesh-basic
,:mesh-lambert
,:mesh-standard
.
{} (:kind :mesh-standard)
:opacity 0.9
:transparent true
:roughness 0.5
:metalness 0.9
:color 0x9050c0
{} (:kind :mesh-lambert)
:color 0x808080
:opacity 0.6
{} (:kind :mesh-basic)
:color 0xffff55
:opacity 0.8
:transparent true
{} (:kind :line-dashed)
:color 0xccccff
:opacity 0.3
:transparent true
:linewidth 2
:gapSize 0.5
:dashSize 0.5
{} (:kind :line-basic)
:color 0xaaaaff
:opacity 0.9
:transparent true
For MeshLine, there's an extra material.
Color
Hex color in number format:
do 0x0000ff
or generate from HSL color:
quatrefoil.core/hslx 200 80 80
HSLuv color based on hsluv:
quatrefoil.core/hsluvx 300 70 90
HCL color is also supported based on d3-color:
quatrefoil.core/hclx 300 70 90
Geometries
a list of geometries
box
box
box $ {}
:width 10
:height 10
:depth 10
:material Material
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
buffer-object
buffer-object
based onBufferGeometry
and flat-values
is a macro for turning lists into a flat one.
buffer-object $ {}
:vertices $ flat-values
0 0 0
10 0 0
5 0 8
5 8 0
:indices $ flat-values
0 1 2
0 2 3
1 2 3
group
group
, takes children elements:
group $ {}
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
line
line $ {}
:points $ [][]
10 20 10
100 140 0
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
MeshLine
Based on https://github.com/spite/THREE.MeshLine
Extra npm module is required:
yarn add meshline
to render mesh line:
mesh-line $ {}
:points $ [] ([] 0 0 0) ([] 3 3 4) ([] 1 4 6) ([] -2 8 0) ([] 2 5 1)
:position $ [] 5 -10 0
:material $ {} (:kind :mesh-line) (:color 0xaaaaff)
:transparent true
:opacity 0.4
:depthTest true
:lineWidth 0.5
implemented with module meshline.
line-segments
Based on https://threejs.org/docs/#api/en/objects/LineSegments.
Pass points in pairs:
line-segments $ {}
:segments $ []
[] ([] 0 0 0) ([] 3 3 4)
[] ([] 1 4 6) ([] -2 8 0)
:position $ [] 10 -10 0
:material $ {} (:kind :line-dashed)
:color 0xaaaaff)
:opacity 0.9
:transparent true
:linewidth 4
:gapSize 0.5
:dashSize 0.5
or pass even number of points:
line-segments $ {}
:points $ [] ([] 0 0 0) ([] 3 3 4) ([] 1 4 6) ([] -2 8 0)
:position $ [] 15 -10 0
:material $ {} (:kind :line-dashed)
:color 0xaaaaff
:opacity 0.9
:transparent true
:linewidth 4
:gapSize 0.5
:dashSize 0.5
sphere
sphere $ {}
:radius 10
:width-segments 10
:height-segments 10
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
parametric
parametric
for parametric geometry
parametric $ {}
:func $ fn (u v)
[] 0 0 0
:slices 10
:stacks 10
plane-reflector
plane-reflector $ {}
:width 40
:height 40
:color 0xffaaaa
:position $ [] 0 0 0
:rotation $ [] 0 0 0
polyhedron
polyhedron $ {}
:vertices $ [][] (8 4 -2)
4 9 -3
-5 -2 -4
4 2 8
:indices $ [][] (0 1 2)
0 1 3
0 2 3
1 2 3
:radius 10
:detail 0
:position $ [] 20 10 10
:material Material
scene
scene
, which refers to a global scene element:
scene ({})
shape
shape $ {}
:path $ [][]
:move-to 0 0
:line-to 10 10
:line-to 10 5
:line-to 0 0
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
spline
spline $ {}
:points $ [][]
10 20 10
100 140 0
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
text
text $ {}
:text |Demo
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
:size 4
:depth 0.5
torus
torus $ {}
:r1 10
:r2 2
:s1 20
:s2 40
:arc $ * 2 &PI
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
tube
tube $ {}
:points-fn $ fn (radio factor)
[] (* factor radio) 0 0
:factor 1
:radius 0.8
:tubular-segments 400
:radial-segments 20
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
:material Material
shader-mesh
Element to render RawShaderMaterial. Inspired by:
- https://threejs.org/examples/#webgl_buffergeometry_rawshader
- https://twitter.com/JimYuan11/status/1523349593496293376
This feature is experimental and might require future changes since lack of familiarity to shaders.
Usage
shader-mesh $ {}
:attributes $ []
{} (:id :position) (:size 3) (:type :f32)
:buffer $ concat &
[] ([] -10 -20 0) ([] 50 0 0) ([] 13 30 0)
{} (:id :color) (:size 4) (:type :u8)
:buffer $ concat &
[] ([] 1 0 0 1) ([] 0 1 0 1) ([] 0 0 1 1 )
:material $ {} (:kind :raw-shader)
:uniforms $ {}
:vertexShader $ inline-shader "\"demo.vert"
:fragmentShader $ inline-shader "\"demo.frag"
:wireframe false
:transparent false
Demo of shaders:
// vertex
precision mediump float;
precision mediump int;
uniform mat4 modelViewMatrix; // optional
uniform mat4 projectionMatrix; // optional
attribute vec3 position;
attribute vec4 color;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vPosition = position;
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
// fragment
precision mediump float;
precision mediump int;
uniform float time;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vec4 color = vec4( vColor );
// color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
gl_FragColor = color;
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
some-object
It actually renders a group
element with "value" element inside. If "value" element is not loaded, then nothing got mounted. Actual element is stored in the atom quatrefoil.globals/*loaded-objects
with a key.
This feature is currently used in rendering GLTF models created via three.js with loaders.
some-object $ {} (:key :sakura)
:loaded? $ some? (get @*loaded-objects :sakura)
:position $ [] 0 -100 -100
:scale $ [] 200 200 200
the fields
key
specifies the key of object inside*loaded-objects
loaded?
is only used to trigger updates, not really being used
Components
with options and logics.
Control Point
move point's visual position on canvas:
quatrefoil.comp.control/comp-pin-point
{}
:position $ :p0 state
:speed 0.1
:color 0xffaaaa
:opacity 0.8
:radius 1
:label |A
:text-color $ hslx 20 90 80
fn (next d!)
d! cursor $ assoc state :p0 next
change value using a point:
quatrefoil.comp.control/comp-value
{}
:radius 1
:value (:v0 state)
:position $ [] 10 0 0
:speed 0.2
:bound $ [] -2 20
:color $ hslx 200 90 70
:text-color $ hslx 20 90 80
:show-text? false
:fract-length 2
:opacity 0.8
:label |A
fn (v1 d!)
d! cursor $ assoc state :v0 v1
change a 2d value using a point:
quatrefoil.comp.control/comp-value-2d
{}
:value $ :v1 state
:position $ [] 0 10 0
:speed 0.2
:color 0xccaaff
:fract-length 2
:label |A
fn (v d!)
d! cursor $ assoc state :v1 v
controls a bool value:
comp-switch
{} (:label "\"Status") (:color 0xaa88ff)
:value $ :on? state
:text-color 0xaa88ff
:position $ [] 20 0 0
fn (v d!)
d! cursor $ assoc state :on? v
Lights
ambient-light
ambient-light $ {}
:color 0xaaaaff
:intensity 1
point-light
point-light $ {}
:color 0xaaaaff
:intensity $ 1
:distance 100
:position $ [] 0 0 0
rect-area-light
rect-area-light $ {}
:color 0xaaaaff
:intensity 1
:width 100
:height 100
:look-at $ [] 100 100 100
:position $ [] 0 0 0
:rotation $ [] 0 0 0
:scale $ [] 1 1 1
set-perspective-camera!
Initialize camera:
ns app.main
:require
quatrefoil.dsl.object3d-dom :refer $ set-perspective-camera!
set-perspective-camera! $ {} (:fov 45)
:near 0.1
:far 1000
:position $ [] 0 0 100
:aspect $ / js/window.innerWidth js/window.innerHeight
Math
...
Quaternions
Based on https://threejs.org/docs/#api/en/math/Quaternion
w
is the factor:
[] x y z w
Under the scope quatrefoil.math
:
q+
add multiple quaternionsq-
q*
&q*
add 2 quaternions&q+
&q-
q-inverse
q-conjugate
q-length
q-length2
length without call sqrtq-scale
takes a quaternion and a scalarv+
add multiple vectorsv-
&v+
add 2 vectors&v-
v-scale
takes a vector and a scalar&c+
add 2 complex numbers&c-
&c*
c-length
c-length2
c-conjutate