npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aredtech/build-forge

v0.1.0

Published

Framework-agnostic Three.js building adapter for place, floor, feature, and marker data.

Readme

Three.js Building Adapter

Framework-agnostic TypeScript utilities for turning place, building, floor, feature, and marker data into Three.js-compatible building objects.

Installation

npm install @aredtech/build-forge three

three is a peer dependency so host applications can control the renderer version they already use.

Core Usage

import { createBuilding3D } from "@aredtech/build-forge";

const building = createBuilding3D({ place });
const local = building.adapter.toLocalPosition({ lat: 55.7175129, lon: 13.2137595 });

scene.add(building.group);
console.log(building.floors.size);

Host applications own scene, camera, renderer, controls, data fetching, and framework lifecycle. The package returns a root THREE.Group, named child groups, generated object maps, adapter methods, metadata, and structured warnings without taking over rendering.

Coordinates

The default local coordinate system maps east to positive x, north to positive z, and height to positive y. Inputs can use { lat, lon }, { lat, lng }, { latitude, longitude }, [lat, lon], or [lat, lon, extra]. Local coordinates are meter-like values relative to a resolved origin.

React Helper

React helpers are optional subpath imports. They are not exported from the package root, so core consumers do not need React.

import { useEffect } from "react";
import { useBuilding3D } from "@aredtech/build-forge/react";

function BuildingLayer({ place, scene }) {
  const building = useBuilding3D({ place });

  useEffect(() => {
    if (!building.result) {
      return undefined;
    }

    scene.add(building.result.group);

    return () => {
      scene.remove(building.result.group);
    };
  }, [building.result, scene]);

  return null;
}

The hook creates core adapter output from place and options. It does not create a Three.js scene, camera, renderer, controls, canvas, data fetcher, or render loop.

Angular Helper

Angular helpers are optional subpath imports. They are not exported from the package root, so core consumers do not need Angular.

import type { Scene } from "three";
import { Building3DService } from "@aredtech/build-forge/angular";
import type { PlaceInfo } from "@aredtech/build-forge";

export class BuildingLayerController {
  constructor(private readonly building3D: Building3DService) {}

  attachBuilding(place: PlaceInfo, scene: Scene): void {
    const result = this.building3D.create({ place });

    scene.add(result.group);
  }
}

The service delegates to createBuilding3D and returns the same core result. Angular applications own components, scene, camera, renderer, controls, routing, data fetching, and lifecycle.

Floors and levels

The adapter builds floor meshes from building.floors[].floorShape[].nodes and adds them to building.group. The root group contains named child groups:

  • build-forge:floors
  • build-forge:features
  • build-forge:markers

The same objects are available through building.groups.floors, building.groups.features, and building.groups.markers. Public maps remain available as building.floors, building.features, and building.markers.

The default floor render mode is flat, which creates a polygon surface on the XZ plane. Callers can request slab mode with options.floors.renderMode; the default slab thickness is 0.2m.

The default level resolver is auto: use floorLabel when present, otherwise floorIndex. Callers can choose floorLabel, floorIndex, or a custom resolver function through options.floors.levelResolver.

Floor y positions use this height fallback order: floor.floorHeight, building.calculatedFloorHeight, options.floors.defaultFloorHeight, then 3m. The default stacking strategy is index-based, with cumulative available through options.floors.stacking.

Generated building metadata is available at building.metadata and includes building id/name, origin, coordinate system, units, resolved levels, local bounds, and the source place object. Generated Three.js objects store source metadata under object.userData.buildForge; floors include sourceType, id, level, floor metadata, and the original source floor reference.

Feature Model

Feature data is read from place.elements by default. The feature model accepts OSM-like node, way, and relation elements:

  • node elements provide geographic coordinates for ways.
  • open way elements render as thin unlit feature lines.
  • Closed ways are preserved as metadata-only wrappers in Phase 4.
  • relation geometry rendering is deferred; relations are preserved as metadata-only wrappers.

Renderable feature ways need tags.level; the value resolves through the same generated floor level registry used by floors and markers. Invalid geometry, missing node references, or unresolved levels append warnings and skip rendering by default. In strict mode, validation-style feature errors throw.

const building = createBuilding3D({
  place: {
    ...place,
    elements: [
      { id: "n1", type: "node", lat: 55.69945, lon: 13.0498 },
      { id: "n2", type: "node", lat: 55.69946, lon: 13.04981 },
      { id: "feature-open", type: "way", nodes: ["n1", "n2"], tags: { level: "G", highway: "footway" } }
    ]
  }
});

const feature = building.adapter.getFeature("feature-open");
console.log(feature?.object.userData.buildForge.sourceType);

Callers can customize feature interpretation with options.features.resolver. The resolver returns constrained instructions only; it does not create arbitrary THREE.Object3D instances.

const building = createBuilding3D({
  place,
  options: {
    features: {
      resolver(element, context) {
        if (element.tags?.ignore === "true") {
          return { kind: "skip" };
        }

        if (element.tags?.indoor === "room") {
          return { kind: "preserve" };
        }

        return context.defaultInstruction.kind === "line"
          ? { kind: "line", color: 0x4f7cff, yOffset: 0.05 }
          : context.defaultInstruction;
      }
    }
  }
});

Generated feature metadata is available in building.features, building.groups.features, building.adapter.getFeature(id), and object.userData.buildForge.

Deferred feature capabilities include relation geometry, room/polygon surfaces, tag-specific default visuals, local feature-node coordinates, snapping, trails, labels, and feature-type visibility.

Markers

Markers are optional runtime objects in building.markers and building.groups.markers.

const marker = building.adapter.upsertMarker({
  id: "device-1",
  lat: 55.699454602193,
  lon: 13.049802649766,
  level: "G",
  metadata: { battery: 88 }
});

building.adapter.updateMarker("device-1", { local: { x: 2, z: 3 }, level: "G" });
building.adapter.removeMarker("device-1");
building.adapter.clearMarkers();

Supported marker positions are:

  • geographic lat/lon plus level
  • local { x, z } plus level
  • explicit local { x, y, z }

Default markers are unlit sphere meshes, so lights are not required. Configure default marker appearance with options.markers.color, options.markers.radius, and options.markers.yOffset.

Custom marker objects can be created with options.markers.renderer. The renderer is called when a marker object is created; normal updateMarker calls move the existing object and preserve object identity.

const building = createBuilding3D({
  place,
  options: {
    markers: {
      renderer: (input, context) => {
        const object = new THREE.Object3D();
        object.position.copy(context.position);
        return object;
      }
    }
  }
});

Marker failures return null or false and append warnings to building.warnings. In strict mode, unresolved marker levels throw. Removal and clear operations detach marker objects from the marker group but do not dispose custom geometry or materials by default.

Warnings and Strict Mode

By default, invalid geometry, unresolved levels, missing feature nodes, unsupported material changes, and invalid marker updates append structured warnings to building.warnings where possible. The adapter skips invalid objects instead of failing the whole build.

const building = createBuilding3D({
  place,
  options: {
    strict: true
  }
});

Use strict: true when ingestion failures should throw immediately during development, tests, or controlled data imports.

Visibility

Floor visibility controls operate only on generated floor meshes; markers remain independently visible.

building.adapter.hideFloor("G");
building.adapter.showFloor("G");
building.adapter.showOnlyFloor("G");
building.adapter.showAllFloors();
building.adapter.setFloorOpacity("G", 0.4);

setFloorOpacity clamps values to 0..1, mutates compatible existing materials, and updates the material transparent state. Unsupported material mutation returns false and appends a FLOOR_OPACITY_UNSUPPORTED warning.

Deferred Capabilities

Room anchors, marker snapping, marker animation, marker trails, marker labels, marker-kind visibility, and feature-type visibility are not part of this phase. They are intentionally not exposed as supported APIs yet.

Core Example

See examples/core-usage.ts for a tiny root API example using a real fixture without creating a scene, camera, renderer, controls, or UI.

Verification

npm test
npm run build
npm run check:declarations
npm run pack:dry-run
npm run smoke:import
npm run verify