Skip to content

Commit

Permalink
perf: ⚡ Improve performance by using Points instead of individual sph…
Browse files Browse the repository at this point in the history
…eres
  • Loading branch information
TeddyHuang-00 committed Feb 17, 2024
1 parent 53e8884 commit 77ec5fa
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 88 deletions.
39 changes: 13 additions & 26 deletions attractors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod utils;
extern crate console_error_panic_hook;

use strum::EnumString;
use utils::{random_color, random_number};
use utils::random_number;
use wasm_bindgen::prelude::*;

type AttractorSystem = dyn Fn([f64; 3]) -> [f64; 3];
Expand Down Expand Up @@ -164,7 +164,7 @@ pub enum AttractorName {
#[wasm_bindgen]
pub struct Attractor {
points: Vec<f64>,
colors: Vec<String>,
colors: Vec<f64>,
system: Box<AttractorSystem>,
}

Expand Down Expand Up @@ -203,24 +203,18 @@ impl Attractor {

Attractor {
points: Vec::with_capacity(30_000),
colors: Vec::with_capacity(10_000),
colors: Vec::with_capacity(30_000),
system: lorenz(),
}
}

pub fn init_points(&mut self, n: usize, range: f64) {
self.points.clear();
for _ in 0..n {
for __ in 0..3 {
self.points.push(random_number(-range, range));
}
}
(0..3 * n).for_each(|_| self.points.push(random_number(-range, range)));
}
pub fn init_colors(&mut self, n: usize) {
self.colors.clear();
for _ in 0..n {
self.colors.push(random_color());
}
(0..3 * n).for_each(|_| self.colors.push(random_number(0.0, 1.0)));
}
pub fn set_system(&mut self, name: &str) {
self.system = match AttractorName::from_str(name) {
Expand All @@ -247,22 +241,15 @@ impl Attractor {
}
pub fn step(&mut self, dt: f64) {
let dt = dt.min(0.02); // limit the maximum dt to avoid numerical instability
for i in 0..self.points.len() / 3 {
let mut v = [
self.points[3 * i],
self.points[3 * i + 1],
self.points[3 * i + 2],
];
v = self.solver(v, dt);
self.points[3 * i] = v[0];
self.points[3 * i + 1] = v[1];
self.points[3 * i + 2] = v[2];
}
(0..self.points.len()).step_by(3).for_each(|i| {
let v = self.solver(self.points[i..i + 3].try_into().unwrap(), dt);
self.points[i..i + 3].copy_from_slice(&v);
});
}
pub fn points(&self) -> Vec<f64> {
self.points.clone()
pub fn points(&self) -> Vec<f32> {
self.points.iter().map(|x| *x as f32).collect()
}
pub fn colors(&self) -> Vec<String> {
self.colors.clone()
pub fn colors(&self) -> Vec<f32> {
self.colors.iter().map(|x| *x as f32).collect()
}
}
12 changes: 0 additions & 12 deletions attractors/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,3 @@ pub fn random_number(min: f64, max: f64) -> f64 {
let mut rng = rand::thread_rng();
rng.gen::<f64>() * (max - min) + min
}

pub fn random_color() -> String {
let r = random_number(0.25, 1.0);
let g = random_number(0.25, 1.0);
let b = random_number(0.25, 1.0);
let max = r.max(g).max(b);
let r: i8 = ((r / max) * 255.0).floor() as i8;
let g: i8 = ((g / max) * 255.0).floor() as i8;
let b: i8 = ((b / max) * 255.0).floor() as i8;
let hex = format!("#{:02x}{:02x}{:02x}", r, g, b);
hex
}
4 changes: 2 additions & 2 deletions components/Camera.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
:look-at="[0, 0, 0]"
/>
<OrbitControls />
<TresAmbientLight :intensity="1" />
<TresDirectionalLight v-light-helper />
<TresAmbientLight v-light-helper />
<!-- <TresDirectionalLight v-light-helper /> -->
</template>

<script setup lang="ts"></script>
Expand Down
59 changes: 24 additions & 35 deletions components/Canvas.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
<template>
<TresCanvas
window-size
preset="realistic"
class="absolute left-0 top-0"
clear-color="#0f0f0f"
>
<Camera />
<Axes />
<TresGroup v-if="rawCoords.length > 0">
<TresMesh :position="convertCoords(i) as Vec3D" v-for="i in indices">
<TresSphereGeometry :args="[pointSize, detailLevel, detailLevel]" />
<TresMeshToonMaterial :color="colors[i]" />
</TresMesh>
</TresGroup>
</TresCanvas>
<Suspense>
<TresCanvas
window-size
preset="realistic"
class="absolute left-0 top-0"
clear-color="#0f0f0f"
>
<Camera />
<Axes />
<TresGroup v-if="rawCoords.length > 0">
<TresPoints>
<!-- <TresBufferGeometry :position="new THREE.BufferAttribute(rawCoords, 3)" /> -->
<TresBufferGeometry :position="[rawCoords, 3]" :color="[colors, 3]" />
<TresPointsMaterial :size="pointSize" :vertexColors="true" />
</TresPoints>
</TresGroup>
</TresCanvas>
</Suspense>
</template>

<script setup lang="ts">
Expand All @@ -22,28 +25,18 @@ const timeSpeed = useTimeSpeed();
const initRange = useInitialRange();
const pointNumber = usePointNumber();
const pointSize = usePointSize();
const detailLevel = useDetailLevel();
const attrctrSelection = useAttractorSelection();
const colors = ref([] as string[]);
const rawCoords = ref(Float64Array.from([]) as Float64Array);
const indices = ref(Array.from(Array(pointNumber.value).keys()));
const convertCoords = (i: number) => {
return [
rawCoords.value.at(3 * i),
rawCoords.value.at(3 * i + 1),
rawCoords.value.at(3 * i + 2),
];
};
const colors = shallowRef(Float32Array.from([]) as Float32Array);
const rawCoords = shallowRef(Float32Array.from([]) as Float32Array);
import init, { Attractor } from "attractors";
onMounted(async () => {
await init();
const attractor = Attractor.new();
const getRawCoords = () => attractor.points() as unknown as Float64Array;
const getColors = () => attractor.colors() as unknown as string[];
const getRawCoords = () => attractor.points() as unknown as Float32Array;
const getColors = () => attractor.colors() as unknown as Float32Array;
// Initialize the attractor points
attractor.init_points(pointNumber.value, initRange.value);
Expand All @@ -57,7 +50,6 @@ onMounted(async () => {
rawCoords.value = getRawCoords();
attractor.init_colors(pointNumber.value);
colors.value = getColors();
indices.value = Array.from(Array(pointNumber.value).keys());
},
{ debounce: 500, maxWait: 1000 },
);
Expand All @@ -75,12 +67,9 @@ onMounted(async () => {
});
// Main animation loop
const { onLoop, pause, resume } = useRenderLoop();
const { onLoop } = useRenderLoop();
const [isPaused, togglePause] = useToggle(false);
onKeyStroke(" ", () => {
togglePause();
isPaused.value ? pause() : resume();
});
onKeyStroke(" ", () => togglePause());
onLoop(({ delta, elapsed, clock }) => {
if (isPaused.value) return;
attractor.step(timeSpeed.value * delta);
Expand Down
11 changes: 0 additions & 11 deletions components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,6 @@
</UBadge>
</UTooltip>
<URange :min="0.01" :max="1" v-model="pointSize" :step="0.01" />
<UTooltip
text="Number of segments in the sphere for each point"
:popper="{ placement: 'right', arrow: true }"
>
Detail level:
<UBadge variant="solid" class="mx-1">
{{ detailLevel }}
</UBadge>
</UTooltip>
<URange :min="3" :max="16" v-model="detailLevel" :step="1" />

<UDivider>
<Icon name="material-symbols:keyboard" size="24" />
Expand Down Expand Up @@ -232,7 +222,6 @@ const timeSpeed = useTimeSpeed();
const initRange = useInitialRange();
const pointNumber = usePointNumber();
const pointSize = usePointSize();
const detailLevel = useDetailLevel();
const keyBindings = [
{
Expand Down
3 changes: 1 addition & 2 deletions composables/useStates.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export const useTimeSpeed = () => useState("timeSpeed", () => 0.2);
export const useInitialRange = () => useState("initialRange", () => 1);
export const usePointNumber = () => useState("pointNumber", () => 1000);
export const usePointSize = () => useState("pointSize", () => 0.1);
export const useDetailLevel = () => useState("detailLevel", () => 6);
export const usePointSize = () => useState("pointSize", () => 0.25);
export const useAttractorSelection = () =>
useState("attractorSelection", () => "lorenz" as nameAttractor);

0 comments on commit 77ec5fa

Please sign in to comment.