Browse Source

in stats-world, fix the location computations to be based on Give attributes

pull/18/head
Trent Larson 2 years ago
parent
commit
8f3a11bb98
  1. 13
      src/components/World/World.js
  2. 139
      src/components/World/components/objects/landmarks.js

13
src/components/World/World.js

@ -15,12 +15,13 @@ import { createRenderer } from "./systems/renderer.js";
const COLOR1 = "#dddddd"; const COLOR1 = "#dddddd";
const COLOR2 = "#0055aa"; const COLOR2 = "#0055aa";
const PLATFORM_BORDER = 10;
const PLATFORM_EDGE_FOR_UNKNOWNS = 10;
const PLATFORM_SIZE = 100; // note that the loadLandmarks calculations may still assume 100
class World { class World {
constructor(container, vue) { constructor(container, vue) {
this.PLATFORM_BORDER = 5;
this.PLATFORM_EDGE_FOR_UNKNOWNS = 10;
this.PLATFORM_SIZE = 100; // note that the loadLandmarks calculations may still assume 100
this.update = this.update.bind(this); this.update = this.update.bind(this);
// Instances of camera, scene, and renderer // Instances of camera, scene, and renderer
@ -49,9 +50,11 @@ class World {
// Terrain Instance // Terrain Instance
const terrain = createTerrain({ const terrain = createTerrain({
color: COLOR1, color: COLOR1,
height: PLATFORM_SIZE + PLATFORM_BORDER * 2, height: this.PLATFORM_SIZE + this.PLATFORM_BORDER * 2,
width: width:
PLATFORM_SIZE + PLATFORM_BORDER * 2 + PLATFORM_EDGE_FOR_UNKNOWNS * 2, this.PLATFORM_SIZE +
this.PLATFORM_BORDER * 2 +
this.PLATFORM_EDGE_FOR_UNKNOWNS * 2,
}); });
this.loop.updatables.push(controls); this.loop.updatables.push(controls);

139
src/components/World/components/objects/landmarks.js

@ -1,21 +1,39 @@
import axios from "axios"; import axios from "axios";
import * as R from "ramda";
import * as THREE from "three"; import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader";
import * as SkeletonUtils from "three/addons/utils/SkeletonUtils"; import * as SkeletonUtils from "three/addons/utils/SkeletonUtils";
import * as TWEEN from "@tweenjs/tween.js"; import * as TWEEN from "@tweenjs/tween.js";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
import { accountsDB, db } from "@/db";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
const ANIMATION_DURATION_SECS = 10; const ANIMATION_DURATION_SECS = 10;
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/";
export async function loadLandmarks(vue, world, scene, loop) { export async function loadLandmarks(vue, world, scene, loop) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
try { try {
await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
const activeDid = settings?.activeDid || "";
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined");
const token = await accessToken(identity);
const url = const url =
endorserApiServer + "/api/v2/report/claims?claimType=GiveAction"; endorserApiServer + "/api/v2/report/claims?claimType=GiveAction";
const headers = { "Content-Type": "application/json" }; const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
const resp = await axios.get(url, { headers: headers }); const resp = await axios.get(url, { headers: headers });
if (resp.status === 200) { if (resp.status === 200) {
console.log("resp.data.data", resp.data.data);
const minDate = resp.data.data[resp.data.data.length - 1].issuedAt; const minDate = resp.data.data[resp.data.data.length - 1].issuedAt;
const maxDate = resp.data.data[0].issuedAt; const maxDate = resp.data.data[0].issuedAt;
const minTimeMillis = new Date(minDate).getTime(); const minTimeMillis = new Date(minDate).getTime();
@ -33,24 +51,27 @@ export async function loadLandmarks(vue, world, scene, loop) {
//const modelLoc = "/models/coreopsis-flower.glb", // 3 flowers //const modelLoc = "/models/coreopsis-flower.glb", // 3 flowers
// modScale = 2; // modScale = 2;
//const modelLoc = "/models/a_bush/scene.gltf", // purple leaves //const modelLoc = "/models/a_bush/scene.gltf", // purple leaves
// modScale = 15, // modScale = 15;
// calculate positions for each claim, especially because some are random
const locations = resp.data.data.map((claim) =>
locForGive(claim, world.PLATFORM_SIZE, world.PLATFORM_EDGE_FOR_UNKNOWNS)
);
// eslint-disable-next-line @typescript-eslint/no-this-alias // eslint-disable-next-line @typescript-eslint/no-this-alias
const parentWorld = world;
loader.load( loader.load(
modelLoc, modelLoc,
function (gltf) { function (gltf) {
gltf.scene.scale.set(0, 0, 0); gltf.scene.scale.set(0, 0, 0);
for (const claim of resp.data.data) { for (let i = 0; i < resp.data.data.length; i++) {
// claim is a GiveServerRecord (see endorserServer.ts)
const claim = resp.data.data[i];
const newPlant = SkeletonUtils.clone(gltf.scene); const newPlant = SkeletonUtils.clone(gltf.scene);
const randomness = claim.id.substring(10);
const x = const loc = locations[i];
(100 * BASE32.indexOf(randomness.substring(0, 1))) / 32 - 50; newPlant.position.set(loc.x, 0, loc.z);
const z =
(100 * BASE32.indexOf(randomness.substring(8, 9))) / 32 - 50; world.scene.add(newPlant);
newPlant.position.set(x, 0, z);
parentWorld.scene.add(newPlant);
const timeDelayMillis = const timeDelayMillis =
fakeRealRatio * fakeRealRatio *
(new Date(claim.issuedAt).getTime() - minTimeMillis); (new Date(claim.issuedAt).getTime() - minTimeMillis);
@ -58,7 +79,7 @@ export async function loadLandmarks(vue, world, scene, loop) {
.delay(timeDelayMillis) .delay(timeDelayMillis)
.to({ x: modScale, y: modScale, z: modScale }, 5000) .to({ x: modScale, y: modScale, z: modScale }, 5000)
.start(); .start();
parentWorld.bushes = [...parentWorld.bushes, newPlant]; world.bushes = [...world.bushes, newPlant];
} }
}, },
undefined, undefined,
@ -68,16 +89,14 @@ export async function loadLandmarks(vue, world, scene, loop) {
); );
// calculate when lights shine on appearing claim area // calculate when lights shine on appearing claim area
for (const claim of resp.data.data) { for (let i = 0; i < resp.data.data.length; i++) {
// claim is a GiveServerRecord (see endorserServer.ts) // claim is a GiveServerRecord (see endorserServer.ts)
const claim = resp.data.data[i];
// compute location for this claim const loc = locations[i];
const randomness = claim.id.substring(10);
const x = (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32 - 50;
const z = (100 * BASE32.indexOf(randomness.substring(8, 9))) / 32 - 50;
const light = createLight(); const light = createLight();
light.position.set(x, 20, z); light.position.set(loc.x, 20, loc.z);
light.target.position.set(x, 0, z); light.target.position.set(loc.x, 0, loc.z);
loop.updatables.push(light); loop.updatables.push(light);
scene.add(light); scene.add(light);
scene.add(light.target); scene.add(light.target);
@ -119,6 +138,86 @@ export async function loadLandmarks(vue, world, scene, loop) {
} }
} }
/**
*
* @param giveClaim
* @returns {x:float, z:float} where -50 <= x & z < 50
*/
function locForGive(giveClaim, platformWidth, borderWidth) {
let loc;
if (giveClaim?.claim?.recipient?.identifier) {
// this is directly to a person
loc = locForEthrDid(giveClaim.claim.recipient.identifier);
loc = { x: loc.x - platformWidth / 2, z: loc.z - platformWidth / 2 };
} else if (giveClaim?.object?.isPartOf?.identifier) {
// this is probably to a project
const objId = giveClaim.object.isPartOf.identifier;
if (objId.startsWith(ENDORSER_ENTITY_PREFIX)) {
loc = locForUlid(objId.substring(ENDORSER_ENTITY_PREFIX.length));
loc = { x: loc.x - platformWidth / 2, z: loc.z - platformWidth / 2 };
}
}
if (!loc) {
// it must be outside our known addresses so let's put it somewhere random
const leftSide = Math.random() < 0.5;
loc = {
x: leftSide
? -platformWidth / 2 - borderWidth / 2
: platformWidth / 2 + borderWidth / 2,
z: Math.random() * platformWidth - platformWidth / 2,
};
}
return loc;
}
/**
* Generate a deterministic x & z location based on the randomness in a ULID.
*
* We'll use the first 20 bits for the x coordinate and next 20 for the z.
* That's a bit over a trillion locations... should be enough for pretty
* unique locations for this fun kind of application within a network
* -- though they're not guaranteed unique without the full ID.
* (We're leaving 40 bits for other properties.)
*
* @param ulid
* @returns {x: float, z: float} where 0 <= x & z < 100
*/
function locForUlid(ulid) {
// The random parts of a ULID come after the first 10 characters.
const randomness = ulid.substring(10);
// That leaves 16 characters of randomness, or 80 bits.
// We're currently only using 32 possible x and z values
// because the display is pretty low-fidelity at this point.
// Similar code is below.
const x = (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32;
const z = (100 * BASE32.indexOf(randomness.substring(4, 5))) / 32;
return { x, z };
}
/**
* See locForUlid
* @param did
* @returns {x: float, z: float} where 0 <= x & z < 100
*/
function locForEthrDid(did) {
// "did:ethr:0x..."
if (did.length < 51) {
return { x: 0, z: 0 };
} else {
const randomness = did.substring(11);
// We'll take the first 4 bits for 16 possible x & z values.
const xOff = parseInt(Number("0x" + randomness.substring(0, 1)), 10);
const x = (xOff * 100) / 16;
// ... and since we're reserving 20 bits total for x, start with character 5.
const zOff = parseInt(Number("0x" + randomness.substring(5, 6)), 10);
const z = (zOff * 100) / 16;
return { x, z };
}
}
function createLight() { function createLight() {
const light = new THREE.SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0); const light = new THREE.SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0);
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function

Loading…
Cancel
Save