Skip to content

Snippets

hanshenSun edited this page Nov 4, 2021 · 18 revisions

Navigate to a directory

cd "<FULL_PATH_TO_DIRECTORY>"

Initialize a node project

npm init

Initialize a git repo

git init

Setup a git remote

git remote add origin "<YOUR_REPO_URL>"

Add all changed files

git add --a

Make a git commit

git commit -m "<YOUR_COMMIT_MESSAGE>"

Push changes to remote

git push

Push changes and link branches

git push -u origin <YOUR_BRANCH_NAME>

Create an empty file

echo "" > <FILENAME_WITH_EXTENSION>

Basic nodejs server

const http = require('http');

const requestListener = function (req, res) {
  res.writeHead(200);
  res.end('Hello, World!');
}

const server = http.createServer(requestListener);
server.listen(8080);

Install vue cli globally

npm install -g @vue/cli

Setup a vue project in an existing npm project

vue create .

Start vue development server

npm run serve

Install backend packages

npm install concurrently dotenv express nodemon body-parser

Sample express server

require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");

const DIR = "dist";
const PORT = process.env.PORT || 8080;

const app = express();
app.use(express.static(DIR));

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.get("/api/test", function(req, res) {
  console.log("Received request");
  res.send("Test successful!");
});

app.listen(PORT, () => {
  console.log(`listening on ${PORT}`);
});

Vue config file

require("dotenv").config();

const PORT = process.env.PORT || 8080;

module.exports = {
  devServer: {
    proxy: {
      "/api/": {
        target: `http://localhost:${PORT}/`,
        logLevel: "debug",
      },
    },
  },
};

Build vue project for production

npm run build

Install threejs

npm install three

ThreeJS Scene Sample

https://jsfiddle.net/amitlzkpa/ot698g4d/

Add lights to threejs scene

scene.add(new THREE.AmbientLight(0xffffff));
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(-20, 40, 0);
scene.add(light);

Add a threejs sphere to the scene

let geometry = new THREE.SphereGeometry(400, 32, 32);
let material = new THREE.MeshBasicMaterial({
  color: 0xffff00,
  wireframe: true
});
let sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);

Add a polar array of threejs spheres

for (let j = 1; j < 6; j++) {
  for (let i = 0; i < 24; i++) {
    let geometry = new THREE.SphereGeometry(40, 4, 4);
    let material = new THREE.MeshBasicMaterial({
      color: 0xffff00,
      wireframe: true
    });
    let sphere = new THREE.Mesh(geometry, material);
    let r = i / 24;
    sphere.position.x = Math.cos(r * Math.PI * 2) * (200 * j);
    sphere.position.z = Math.sin(r * Math.PI * 2) * (200 * j);
    scene.add(sphere);
  }
}

Add a GLTF model to threejs scene

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

...

let sceneObject = new THREE.Object3D();
let gltfLoader = new GLTFLoader();
let url = "<YOUR_PATH_TO_MODEL>";
gltfLoader.load(url, gltf => {
  let modelData = gltf.scene;
  sceneObject.add(modelData);
  scene.add(sceneContent);
});

Add a 3DM model to threejs scene

import { Rhino3dmLoader } from "three/examples/jsm/loaders/3DMLoader.js";

...

let sceneObject = new THREE.Object3D();
let rh3dmLoader = new Rhino3dmLoader();
rh3dmLoader.setLibraryPath(
  "https://cdn.jsdelivr.net/npm/[email protected]/"
);
rh3dmLoader.load("<YOUR_PATH_TO_MODEL>", function(model) {
    sceneObject.add(model);
    sceneObject.rotation.x = -Math.PI / 2;
    sceneContent = sceneObject;
    scene.add(sceneContent);
  });
});

Add black edges to threejs scene

let edgesObj = new THREE.Object3D();
for ( let i = 0; i < model.children.length; i++ ) {
  let mesh = model.children[i];
  let edges = new THREE.EdgesGeometry( mesh.geometry );
  let line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0x000000 } ) );
  edgesObj.add( line );
}
sceneObject.add(edgesObj);

Zoom All

onBtnClickZoomAll() {
      if (!sceneContent) return;
      // ref: https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/3
      const offset = 1.25;
      const boundingBox = new THREE.Box3();
      boundingBox.setFromObject(sceneContent);
      const center = new THREE.Vector3();
      boundingBox.getCenter(center);
      const size = new THREE.Vector3();
      boundingBox.getSize(size);
      const maxDim = Math.max(size.x, size.y, size.z);
      const fov = camera.fov * (Math.PI / 180);
      let cameraZ = Math.abs((maxDim / 4) * Math.tan(fov * 2));
      cameraZ *= offset;
      camera.position.z = cameraZ;
      const minZ = boundingBox.min.z;
      const cameraToFarEdge = minZ < 0 ? -minZ + cameraZ : cameraZ - minZ;
      camera.far = cameraToFarEdge * 10;
      camera.updateProjectionMatrix();
      if (controls) {
        controls.target = center;
        controls.maxDistance = cameraToFarEdge * 2;
        controls.saveState();
      } else {
        camera.lookAt(center);
      }
    },

Toggle Grid Visibility

updateGridVisibility() {
      if (groundGrid) scene.remove(groundGrid);
      if (this.gridVisibility) {
        let gg = new THREE.GridHelper(10000, 100);
        scene.add(gg);
        groundGrid = gg;
      }
    }

Empty ThreeJS scene wrapped in a vue component

See code sample
// filename: Three.vue
<template>
  <div id="wrapper">
    <div id="container" @mousedown="onMouseDown" @mouseup="onMouseUp"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
window.THREE = THREE;
let container, renderer, scene, camera, controls, composer;
// let sceneContent;
export default {
  data() {
    return {};
  },
  methods: {
    onContainerResize() {
      camera.aspect = container.clientWidth / container.clientHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(container.clientWidth, container.clientHeight);
    },
    init() {
      container = document.getElementById("container");
      camera = new THREE.PerspectiveCamera(
        60,
        container.clientWidth / container.clientHeight,
        10,
        10000000
      );
      camera.position.set(300, 800, 600);
      camera.lookAt(new THREE.Vector3());
      scene = new THREE.Scene();
      scene.add(camera);
      renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.xr.enabled = true;
      renderer.setSize(container.clientWidth, container.clientHeight);
      container.appendChild(renderer.domElement);
      composer = new EffectComposer(renderer);
      composer.addPass(new RenderPass(scene, camera));
      controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.2;
      controls.update();
      window.addEventListener(
        "resize",
        () => {
          this.onContainerResize();
        },
        false
      );
      this.onContainerResize();

      // uncomment below to add a sphere to scene
      // let geometry = new THREE.SphereGeometry(400, 32, 32);
      // let material = new THREE.MeshBasicMaterial({
      //   color: 0xffff00,
      //   wireframe: true
      // });
      // let sphere = new THREE.Mesh(geometry, material);
      // scene.add(sphere);
    },
    animate() {
      renderer.setAnimationLoop(() => {
        controls.update();
        composer.render();
        // renderer.render(scene, camera);
      });
    },
    onMouseDown() {},
    onMouseUp() {}
  },
  mounted() {
    this.init();
    this.animate();
    setTimeout(this.loadModel, 2000);
  }
};
</script>

<style scoped>
#container {
  width: 100%;
  height: 100%;
  padding: 0px;
  margin: 0px;
  overflow: hidden;
}
#wrapper {
  width: 100%;
  height: 100%;
  margin: 0px;
  padding: 0px;
  overflow: hidden;
}
</style>

Firebase.js

import firebase from 'firebase/app';
import 'firebase/storage'
import 'firebase/firestore'
var firebaseui = require('firebaseui');
import "firebaseui/dist/firebaseui.css";


// firebase init
// const firebaseConfig = {
//   apiKey: '',
//   authDomain: '',
//   databaseURL: '',
//   projectId: '',
//   storageBucket: '',
//   messagingSenderId: '',
//   appId: ''
// }

firebase.initializeApp(firebaseConfig);

// utils
const db = firebase.firestore()
const auth = firebase.auth()
var ui = new firebaseui.auth.AuthUI(firebase.auth());
var storage = firebase.storage().ref();

// export utils/refs
export {
  db,
  auth,
  storage,
  ui
}

Initialize FirebaseUI

// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());

Login/out UI

<div style="z-index: 12;top: 10px;right: 10px; position: absolute;">
  <!-- Login button -->
  <v-btn v-show="!user" @click="showLoginOptions = !showLoginOptions" color="success">
    Login
  </v-btn>

  <!-- User avatar after login -->
  <v-menu v-if="user" open-on-hover offset-y>
    <template v-slot:activator="{ on, attrs }">
      <v-btn v-bind="attrs" v-on="on" icon>
        <v-avatar color="red">
          <span class="white--text text-h5">{{user.displayName | getNameInitials}}</span>
        </v-avatar>
      </v-btn>
    </template>

    <!-- Dropdown for logout -->
    <v-list>
      <v-list-item>
        <v-list-item-title>
          <a @click="signoutButtonPressed">Logout </a>                
        </v-list-item-title>
      </v-list-item>
    </v-list>
  </v-menu>
</div> 

Create container for Auth interface

<section id="firebaseui-auth-container" ></section>

Render the FirebaseUI Auth interface, using Oauth Provider

ui.start('#firebaseui-auth-container', {
  signInOptions: [
    // List of OAuth providers supported.
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    firebase.auth.FacebookAuthProvider.PROVIDER_ID,
    firebase.auth.TwitterAuthProvider.PROVIDER_ID,
    firebase.auth.GithubAuthProvider.PROVIDER_ID
  ],
  // Other config options...
});

Get the currently signed-in user

firebase.auth().onAuthStateChanged((user) => {
  if (user) {
    // User is signed in, see docs for a list of available properties
    // https://firebase.google.com/docs/reference/js/firebase.User
    var uid = user.uid;
    // ...
  } else {
    // User is signed out
    // ...
  }
});

Sign out

firebase.auth().signOut().then(() => {
  // Sign-out successful.
  console.log("Sign-out successful");
}).catch((error) => {
  // An error happened.
});

Uploading/ loading/ sharing UI

<!-- Buttons for uploading/ loading/ sharing models -->
<v-row style="margin:10px; position:absolute; left:0px; top:0px; z-index: 12;">
  <v-btn color="primary" @click="showNewModelDialog = true" :disabled="!user"> Upload a New Model </v-btn>
  <v-btn color="secondary" @click="launchModelLoader()" style="margin-left:20px; margin-right:20px" > Load Existing Models </v-btn>
  <v-btn class="mx-2" fab dark small color="indigo" @click="showSharingDialog=true" :disabled="!enableShare">
    <v-icon dark> mdi-share </v-icon>
  </v-btn>
</v-row>

Upload Dialog

<v-dialog v-model="showNewModelDialog" width="500">
  <v-card>
    <v-card-title class="text-h5 grey lighten-2">
      Upload a New Model
    </v-card-title>

    <v-card-text>
      <v-text-field
        v-model="newModelName"
        label="Model Name"
        prepend-icon="mdi-format-text-variant-outline"
        required
      ></v-text-field>

      <!-- File Uploader -->
      <v-file-input
        v-model="filesForUpload"
        placeholder="Upload a model"
        label="File input"
        multiple
        prepend-icon="mdi-paperclip"
        style="margin-top:25px"
      >
        <template v-slot:selection="{ text }">
          <v-chip small label color="primary">
            {{ text }}
          </v-chip>
        </template>
      </v-file-input>        
    </v-card-text>

    <v-progress-linear :active="modelUploading" indeterminate color="deep-purple accent-4" ></v-progress-linear>    
    <v-divider></v-divider>

    <v-card-actions>
      <v-spacer></v-spacer>            
      <v-btn color="primary" text @click="submitFiles();modelUploading=true" :disabled="!newModelName || filesForUpload.length < 1">
        Upload!                       
      </v-btn>
            
    </v-card-actions>
  </v-card>
</v-dialog>

Upload a file to storage

Documentation:

const ref = firebase.storage().ref().child('some-child');

var bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21]);
ref.put(bytes).then((snapshot) => {
  console.log('Uploaded an array!');
});

Our implementation: this.fbStorage.child('models/' + this.filesForUpload[0].name).put(this.filesForUpload[0])

Insert a DB record

Documentation:

// Add a new document with a generated id.
db.collection("cities").add({
    name: "Tokyo",
    country: "Japan"
})
.then((docRef) => {
    console.log("Document written with ID: ", docRef.id);
})
.catch((error) => {
    console.error("Error adding document: ", error);
});

File Loading Dialog

<v-dialog v-model="showModelLoaderDialog" width="500">
  <v-card>
    <v-card-title class="text-h5 grey lighten-2">
      Load Existing Model
    </v-card-title>

    <v-data-table
      :headers="LoadingTableHeaders"
      :items="existingModels"
      :items-per-page="itemsPerPage"
      class="elevation-1"
    >
      <template v-slot:item.isPublic="{ item }">
        <v-icon small v-if="item.isPublic"> mdi-check</v-icon>
      </template> 

      <template v-slot:item.loadingAction="{ item }">
        <v-icon small @click="setCurrentModel(item)"> mdi-plus-circle</v-icon>
      </template>         
    </v-data-table>

    <v-progress-linear v-show="modelLoading" color="deep-purple accent-4" indeterminate rounded height="6"></v-progress-linear>
    <v-divider></v-divider>
  </v-card>
</v-dialog>

Get all records in DB collection

var docRef = db.collection("cities");

docRef.get().then((doc) => {
    if (doc.exists) {
        console.log("Document data:", doc.data());
    } else {
        // doc.data() will be undefined in this case
        console.log("No such document!");
    }
}).catch((error) => {
    console.log("Error getting document:", error);
});

Get Download URL from Storage

storageRef.child('images/stars.jpg').getDownloadURL()
  .then((url) => {
  })
  .catch((error) => {
    // Handle any errors
  });

Model Sharing Dialog

<v-dialog v-model="showSharingDialog" width="500">
  <v-card>
    <v-card-title class="text-h5 grey lighten-2">
      Manage Access
    </v-card-title>

    <v-card-text v-if="loadedModel">
      <v-switch v-model="loadedModel.isPublic" label="Make this model public?" ></v-switch>            
    </v-card-text>
         
    <v-progress-linear v-show="modelLoading" color="deep-purple accent-4" indeterminate rounded height="6"></v-progress-linear>
    <v-divider></v-divider>

    <v-card-actions>
      <v-spacer></v-spacer>            
      <v-btn color="primary" text @click="saveSharingSettings()"> Save </v-btn>            
    </v-card-actions>
  </v-card>
</v-dialog>

Update a db Record

var washingtonRef = db.collection("cities").doc("DC");

// Set the "capital" field of the city 'DC'
washingtonRef.update({
    capital: true
})
.then(() => {
    console.log("Document successfully updated!");
})
.catch((error) => {
    // The document probably doesn't exist.
    console.error("Error updating document: ", error);
});

Firebase Storage Rule Setup

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;
      allow write: if request.auth != null;
    }
  }
}