UNPKG

@threepipe/plugin-svg-renderer

Version:

Plugins for SVG Rendering of 3d objects for Threepipe

390 lines (334 loc) 10.7 kB
// Author: Axel Antoine // mail: ax.antoine@gmail.com // website: https://axantoine.com // 23/02/2021 // Loki, Inria project-team with Université de Lille // within the Joint Research Unit UMR 9189 CNRS-Centrale // Lille-Université de Lille, CRIStAL. // https://loki.lille.inria.fr // LICENCE: Licence.md // Modified by repalash <palash@shaders.app>. Minor changes, added parameters and error handling. import {ColorRepresentation, PerspectiveCamera} from 'three'; import {SizeLike} from '../../utils'; import {SVGMesh} from '../SVGMesh'; import {Chain, ChainVisibility} from './Chain'; import {ViewEdge} from './ViewEdge'; import {computePolygons, PolygonsInfo} from './operations/computePolygons'; import {setupEdges} from './operations/setupEdges'; // import { ViewPoint } from './ViewPoint_'; import {Polygon} from './Polygon'; import {AssignPolygonInfo, assignPolygons} from './operations/assignPolygons'; import {ChainVisibilityInfo, computeChainsVisibility} from './operations/computeChainsVisibility'; // import { setupPoints } from './operations/setupPoints_'; import {computeMeshIntersections, MeshIntersectionInfo} from './operations/computeMeshIntersections'; import {ViewVertex} from './ViewVertex'; import {createChains} from './operations/createChains' import {find2dSingularities} from './operations/find2dSingularities' import {find3dSingularities} from './operations/find3dSingularities' declare module '../../../three-mesh-halfedge' { export interface Face { viewEdges: ViewEdge[]; } export interface Vertex { viewVertex: ViewVertex; } } export interface ViewmapOptions { updateMeshes?: boolean; ignoreVisibility?: boolean; defaultMeshColor?: ColorRepresentation; creaseAngle?: { min: number, max: number, } } export class ViewmapBuildInfo { totalTime = Infinity; /** Record or times in ms */ times = { updateGeometries: Infinity, updateBVH: Infinity, updateHES: Infinity, setupEdges: Infinity, find3dSingularities: Infinity, find2dSingularities: Infinity, computeChains: Infinity, visibility: Infinity, computePolygons: Infinity, assignPolygons: Infinity, worldTransform: Infinity, meshIntersections: Infinity, setupPoints: Infinity, setupFaceMap: Infinity, }; intersections = new MeshIntersectionInfo(); visibility = { nbTests: Infinity, nbRaycasts: Infinity, }; polygons = { smallAreaIgnored: Infinity, insidePointErrors: Infinity, assigned: Infinity, nonAssigned: Infinity, }; } export interface ProgressInfo { currentStepName: string; currentStep: number; totalSteps: number; } interface ViewmapAction { name: string; process: () => Promise<void>; } export class Viewmap { readonly meshes = new Array<SVGMesh>(); readonly viewEdges = new Array<ViewEdge>(); // readonly viewPointMap = new Map<string, ViewPoint>(); readonly viewVertexMap = new Map<string, ViewVertex>(); readonly chains = new Array<Chain>(); readonly polygons = new Array<Polygon>(); readonly camera = new PerspectiveCamera(); readonly renderSize = {w: 500, h: 500}; readonly options: Required<ViewmapOptions> = { updateMeshes: true, ignoreVisibility: false, defaultMeshColor: 0x555555, creaseAngle: { min: 80, max: 100, } } constructor(options?: ViewmapOptions) { Object.assign(this.options, options); } clear() { this.meshes.clear(); this.viewEdges.clear(); // this.viewPointMap.clear(); this.viewVertexMap.clear(); this.chains.clear(); this.polygons.clear(); } build( meshes: SVGMesh[], camera: PerspectiveCamera, renderSize: SizeLike, info = new ViewmapBuildInfo(), progressCallback?: (progress: ProgressInfo) => void) { this.clear(); this.meshes.push(...meshes); const ud = camera.userData camera.userData = {}; this.camera.copy(camera); camera.userData = ud; this.camera.getWorldPosition(camera.position); this.renderSize.w = renderSize.w; this.renderSize.h = renderSize.h; const actions = this.setupActions(info); info.totalTime = Date.now(); return this.buildAsync(0, actions, info, progressCallback); } skipActions = false private buildAsync( idx: number, actions: ViewmapAction[], info: ViewmapBuildInfo, progressCallback?: (progress: ProgressInfo) => void) { info.totalTime = Date.now(); return new Promise<void>((resolve) => { if (idx < actions.length) { progressCallback && progressCallback({ totalSteps: actions.length, currentStep: idx+1, currentStepName: actions[idx].name }); const res = () => { resolve(this.buildAsync(idx+1, actions, info, progressCallback)); } if(this.skipActions) res() else { // console.info(`Viewmap step ${idx+1}/${actions.length} : ${actions[idx].name}`); actions[idx].process().catch(_ => { // todo handle errors properly depending on the step. // console.error(`Viewmap step ${idx+1}/${actions.length} : ${actions[idx].name} failed`, e); }).then(res) } } else { info.totalTime = Date.now() - info.totalTime; resolve(); } }); } private setupActions(info = new ViewmapBuildInfo()): Array<ViewmapAction> { const actions = new Array<ViewmapAction>(); if (this.options.updateMeshes) { /** * Update Morphed Geometries */ actions.push({ name: "Update Morphed Geometries", process: async() => { const startTime = Date.now(); for (const mesh of this.meshes) { mesh.updateMorphGeometry(); } info.times.updateGeometries = Date.now() - startTime; } }); /** * Update BVH structs */ actions.push({ name: "Update BVH Structures", process: async() => { const startTime = Date.now(); for (const mesh of this.meshes) { mesh.updateBVH(false); } info.times.updateBVH = Date.now() - startTime; } }); /** * Update Halfedge structs */ actions.push({ name: "Update Halfedge Structures", process: async() => { const startTime = Date.now(); for (const mesh of this.meshes) { mesh.updateHES(false); } info.times.updateHES = Date.now() - startTime; } }); /** * Update Halfedge structures to world positions */ actions.push({ name: "Transform local 3d points into world", process: async() => { const startTime = Date.now(); for (const mesh of this.meshes) { for (const vertex of mesh.hes.vertices) { vertex.position.applyMatrix4(mesh.matrixWorld); } } info.times.worldTransform = Date.now() - startTime; } }); } /** * Setup edges */ actions.push({ name: "Setup viewmap edges", process: async() => { const startTime = Date.now(); setupEdges(this, this.options); info.times.setupEdges = Date.now() - startTime; } }); /** * Compute Meshes Intersections */ actions.push({ name: "Compute meshes intersections", process: async() => { const startTime = Date.now(); computeMeshIntersections(this, info.intersections); info.times.meshIntersections = Date.now() - startTime; } }); /** * Find singularities in the 3D space */ actions.push({ name: "Find singularities in the 3d space", process: async() => { const startTime = Date.now(); // this.singularityPoints = find3dSingularities(this); info.times.find3dSingularities = Date.now() - startTime; } }); /** * Find singularity points in the 2d space (image place intersections) * This step creates new points and segments on-the-fly */ actions.push({ name: "Find singularities in the 2d space", process: async() => { const startTime = Date.now(); find2dSingularities(this); info.times.find2dSingularities = Date.now() - startTime; } }); /** * Compute chains from the set of segments: link segments depending * of their connexity and nature */ actions.push({ name: "Create chains", process: async() => { const startTime = Date.now(); createChains(this); info.times.computeChains = Date.now() - startTime; } }); /** * Compute contours visibility using geometry's topology or raycasting if * need. * If ignore visibility is set, set all contours to be visible */ actions.push({ name: "Compute chains visibility", process: async() => { if (!this.options.ignoreVisibility) { const startTime = Date.now(); const visInfo = new ChainVisibilityInfo(); computeChainsVisibility(this, visInfo); info.visibility.nbRaycasts = visInfo.nbRaycasts; info.visibility.nbTests = visInfo.nbTests; info.times.visibility = Date.now() - startTime; } else { this.chains.map(chain => chain.visibility = ChainVisibility.Visible); } } }); /** * Compute the polygons formed by the visible subset of contours */ actions.push({ name: "Compute polygons", process: async() => { const startTime = Date.now(); const polyInfo = new PolygonsInfo(); await computePolygons(this, polyInfo); info.polygons.smallAreaIgnored = polyInfo.smallAreaIgnored; info.polygons.insidePointErrors = polyInfo.insidePointErrors; info.times.computePolygons = Date.now() - startTime; } }); /** * Assign polygons to their corresponding object with raycasting */ actions.push({ name: "Assign Polygons", process: async() => { const startTime = Date.now(); const assignInfo = new AssignPolygonInfo(); assignPolygons(this, this.options, assignInfo); info.polygons.assigned = assignInfo.assigned; info.polygons.nonAssigned = assignInfo.nonAssigned; info.times.assignPolygons = Date.now() - startTime; } }); return actions; } visibleChains() { return this.chains.filter(c => c.visibility === ChainVisibility.Visible); } hiddenChains() { return this.chains.filter(c => c.visibility === ChainVisibility.Hidden); } }