Skip to content

Repository files navigation

Lagopus in Calcit

tiny tool for drawing 3D shapes with WebGPU, wraps on Lagopus.ts.

Dandelions, made previously with Triadica

开发与部署

使用正式 Calcit 0.28.0、Caps 0.1.1、Node.js 24 和 Yarn 4.18.0:

caps --ci
corepack yarn install --immutable
caps verify --toolchain
corepack yarn dev

另开终端运行 corepack yarn watch 可持续编译 Calcit。corepack yarn build 生成前端产物;js-out/ 和 dist/ 不纳入版本控制。源码使用 calcit.cirru 和 deps.cirru。

CI 为生产构建设置 VITE_BASE_URL=https://cos-sh.tiye.me/WebGPU-Art/lagopus/,通过 COS action 的 public-base-url 启用内置上传校验。需要 COS_BUCKET、COS_SECRET_ID、COS_SECRET_KEY 和原有 rsync_private_key secrets。保留原服务器部署路径;PR 仅检查和构建,不上传生产资源。

类型门禁使用 calcit --check-only --warn-dyn-method,并保留全部 18 个公开命名空间的检查。配置边界的 :topology 按下文约定检查为 Tag,再转为 WebGPU 所需字符串;watcher 和字符串拼接使用首选 API 名,不依赖 calcit fix。CI 使用可读的正式 Action release tag(并非不可变 SHA),上传验证仅由 COS action v1.2.0 执行。

本轮本地完整严格检查、140 个公开定义检查和 JS/Vite 生产构建通过;尚未验证 WebGPU 实机渲染或实际 COS/服务器部署。

APIs

:lagopus.alias :refer $ object

object $ {}
  :shader demo-wgsl
  :topology :triangle-list
  :attrs-list $ []
    :: :float32x3 :position
    :: :float32x3 :color
  :data $ []
    :: :vertex (v3 0 0 0) (v3 1 0 0)
    :: :vertex (v3 100 0 0) (v3 0 1 0)
    :: :vertex (v3 0 100 0) (v3 0 0 1)
  • :shader custom shader file string
  • :topology topology symbol, :triangle-list or :line-strip
  • :attrs-list list of attributes, first value providing type, second value is a name(history reason...)
  • :data list of data, each data is a map with keys matching :attrs-list, nested list is supported
  • optional :indices list of indices, each index is a number. nested list is supported
  • :get-params, to add custom params with uniform buffer, for example fn () (js-array 1 1 1 1), notice the values should obey memory alignments

Alternatively, there's also object-writer which provides a writer API, which replaces :data:

  :writer $ fn (write!)
    write! $ []
      :: :vertex (v3 1 1 1)

Shader

Shader file expects 3 parts:

  • params(could be empty)
  • vertex_main, here you call transform_perspective to display point in 3d
  • fragment_main
// to include `lagopus-perpective.wgsl` , provide function `transform_perspective`
#import lagopus::perspective

struct Params {
  _t: f32,
}

@group(0) @binding(0) var<uniform> params: Params;

// structure passing from Vertex to Fragment
struct VertexOut {
  @builtin(position) position: vec4f,
};

@vertex
fn vertex_main(
  @location(0) position: vec3f,
  // data added from attributes put here
) -> VertexOut {
  var output: VertexOut;
  let p = transform_perspective(position.xyz).point_position;
  let scale: f32 = 0.002;

  output.position = vec4(p.xyz * scale, 1.0);
  return output;
}

@fragment
fn fragment_main(vtx_out: VertexOut) -> @location(0) vec4f {
  return vec4f(0.0, 0.0, 0.0, 1.0);
}
Builtin functions

#import lagopus::perspective

  • fn transform_perspective(p: vec3f) -> PointResult

#import lagopus::colors

  • fn hsl2rgb(hsl: vec3f) -> vec3f
  • fn hsl(h: f32, s: f32, l: f32) -> vec3f

#import lagopus::rand

  • fn rand(n: f32) -> f32
  • fn rand_balanced(n: f32) -> f32
  • fn noise(p: f32) -> f32
  • fn rand2(n: vec2<f32>) -> f32
  • fn noise2(n: vec2<f32>) -> f32

#import lagopus::simplex

  • fn simplexNoise2(v: vec2<f32>) -> f32

#import lagopus::hsluv

  • fn hsluvToRgb(tuple: vec3f) -> vec3f based on 360 and 100

Components

Curves
lagopus.comp.curves :refer $ comp-curves comp-polylines break-mark

comp-curves $ {} (; :topology :line-strip)
  :curves $ []
    -> (range 400)
      map $ fn (idx)
        let
            angle $ * 0.1 idx
            r 40
          {}
            :position $ []
              * r $ cos angle
              * 0.6 idx
              * r $ sin angle
            :width 2

Another way of defining lines is comp-polylines that seperate segments with :: :breaks. It does not require "flatterned" list so is supposed to be a little performant.

note that : vertex p is a short form for :: :vertex p since Calcit 0.7.2 .

comp-polylines $ {} (; :topology :line-strip)
  :writer $ fn $ (write!)
    write! $ []
      : vertex ([] 0 0 0) width
      : vertex ([] 100 100 0) width
      , break-mark
      : vertex ([] 0 0 10) width
      : vertex ([] 200 0 10) width
      : vertex ([] 200 20 0) width
      : vertex ([] 100 40 0) width
      : vertex ([] 100 20 200) width
      , break-mark

also if you want an extra mark for controlling colors:

comp-polylines-marked $ {} (; :topology :line-strip)
  :writer $ fn (write!)
    write! $ []
      : vertex ([] 0 0 0) width 0
      : vertex ([] 100 100 0) width 0
      , break-mark
      : vertex ([] 0 0 10) width 2
      : vertex ([] 200 0 10) width 2
      : vertex ([] 200 20 0) width 2
      : vertex ([] 100 40 0) width 2
      : vertex ([] 100 20 200) width 2
      , break-mark
Spots
lagopus.comp.spots :refer $ comp-spots comp-bubbles

comp-spots $ {} (; :topology :line-strip)
  :radius 6
  :vertex-count 8
  :shift 12
  :points $ -> (range 80)
    map $ fn (idx)
      let
          r $ * idx 4
        [] r
          * r $ cos (* 0.1129 idx)
          * r $ sin (* 0.123 idx)

comp-bubbles $ {}
  :bubbles $ -> (range 600)
    map $ fn (idx)
      [] (rand-shift 0 area) (rand-shift 0 area) (rand-shift 0 area) (+ 6 $ rand 120)
Axis
lagopus.comp.curves :refer $ comp-axis

comp-axis $ %some $ {} (:n 20)
  :unit 20

; use default options
comp-axis $ %none
Cube
lagopus.comp.cube :refer $ comp-cube

comp-cube $ {}
  :position $ v3 40 0 0
  :radius 40
Sphere
lagopus.comp.sphere :refer $ comp-sphere

comp-sphere $ {} (; :topology :line-strip)
  :iteration 4
  :radius 160
Plate
lagopus.comp.plate :refer $ comp-plate

comp-plate $ {} (; :topology :line-strip)
  :iteration 20
  :radius 160
  :color $ [] 0.04 0.8 0.6
  :transformer $ fn (i)
    hint-fn $ {}
      :args $ [] 'quaternion.vector/V3
      :return 'quaternion.vector/V3
    v+ i $ v3 0 0 -10
  ; :x-direction $ v3 1 0 0
  ; :y-direction $ v3 0 1 0
  :chromatism 0.14
Controls
lagopus.comp.button :refer $ comp-button comp-slider comp-drag-point

comp-button
  {}
    :position $ v3 240 260 0
    :color $ [] 0.2 0.9 0.6 1
    :size 20
  fn (e d!)
    hint-fn $ {}
      :args $ [] 'JsObject $ :: 'Fn
        {} (:args $ [] 'Enum) (:return 'Unit)
      :return 'Unit
    d! $ :: :tab :sphere

comp-slider
  {} $ :position ([] 0 0 0)
  fn (change on-slide)
    hint-fn $ {}
      :args $ [] 'quaternion.complex/Complex $ :: 'Fn
        {} (:args $ [] 'Enum) (:return 'Unit)
      :return 'Unit
    println |Slide change
    , &unit

comp-drag-point
  {}
    :position $ &map:get state :pos
    :color $ [] 0.6 0.6 1.0 1.0
  fn (move d!)
    hint-fn $ {}
      :args $ [] 'quaternion.vector/V3 $ :: 'Fn
        {} (:args $ [] 'Enum) (:return 'Unit)
      :return 'Unit
    d! $ :: :state cursor $ assoc state :pos move
Stitch
lagopus.comp.stitch :refer $ comp-stitch

comp-stitch $ {}
  :chars $ [] 0xf2dfea34 0xc3c4a59d 0x88737645
  :position $ [] 0 0 0

Cursor

lagopus.cursor :refer $ update-states >>

Math functions in lagopus.math

  • fibo-grid-range total create a list of points constructing Fibonacci Sphere
  • rotate-3d origin axis-0 angle p rotate point p around axis-0
  • rotate-3d-fn origin axis-0 angle create a function to rotate point p

Gamepad Controls

Gamepad

Resources

Shader functions from https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39

License

MIT

About

Calcit binding for lagopus.ts , for WebGPU based toys

Topics

Resources

Stars

3 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages