Skip to content

Commit

Permalink
Merge pull request #1053 from hypar-io/add-more-formatters
Browse files Browse the repository at this point in the history
add more notebook formatters
  • Loading branch information
andrewheumann authored Nov 9, 2023
2 parents 6d10065 + 8f35e5b commit 213f4b7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 195 deletions.
45 changes: 45 additions & 0 deletions Elements/src/extension.dib
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Formatting;
using Elements;
using Elements.Geometry;
using Elements.Geometry.Solids;
using Elements.Serialization.glTF;
using Newtonsoft.Json;
using System;
Expand Down Expand Up @@ -134,6 +135,7 @@ if (KernelInvocationContext.Current is { } currentContext)
double DEFAULT_MODEL_WIDTH = 600;
double DEFAULT_MODEL_HEIGHT = 400;
Material DEFAULT_CURVE_MATERIAL = BuiltInMaterials.XAxis;
Material DEFAULT_SOLID_MATERIAL = BuiltInMaterials.Default;

string GetModelViewerSrc(Model model, double? width=null, double? height=null) {
var gltf = model.ToGlTF();
Expand Down Expand Up @@ -193,6 +195,45 @@ Formatter.Register<Model>((model, writer) => {
writer.Write(src);
}, "text/html");

Formatter.Register<SolidOperation>((solidOp, writer) => {
var model = new Model();
model.AddElement(new GeometricElement { Representation = solidOp, Material= DEFAULT_SOLID_MATERIAL});
var src = GetModelViewerSrc(model);
writer.Write(src);

}, "text/html");
Formatter.Register<Representation>((representation, writer) => {
var model = new Model();
model.AddElement(new GeometricElement { Representation = representation, Material= DEFAULT_SOLID_MATERIAL});
var src = GetModelViewerSrc(model);
writer.Write(src);

}, "text/html");
Formatter.Register<IEnumerable<SolidOperation>>((solidOps, writer) => {
var model = new Model();
foreach(var solidOp in solidOps) {
model.AddElement(new GeometricElement { Representation = solidOp, Material= DEFAULT_SOLID_MATERIAL});
}
var src = GetModelViewerSrc(model);
writer.Write(src);

}, "text/html");
Formatter.Register<Transform>((xform, writer) => {
var model = new Model();
model.AddElements(xform.ToModelCurves());
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<IEnumerable<Transform>>((xforms, writer) => {
var model = new Model();
foreach(var xform in xforms) {
model.AddElements(xform.ToModelCurves());
}
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

void DisplayModel(Model model, double? width=null, double? height=null) {
var src = GetModelViewerSrc(model, width ?? DEFAULT_MODEL_WIDTH, height ?? DEFAULT_MODEL_HEIGHT);
KernelInvocationContext.Current.DisplayAs(src, "text/html");
Expand All @@ -206,3 +247,7 @@ void SetDefaultDisplaySize(double width, double height) {
void SetDefaultCurveMaterial(Material mat) {
DEFAULT_CURVE_MATERIAL = mat;
}

void SetDefaultSolidMaterial(Material mat) {
DEFAULT_SOLID_MATERIAL = mat;
}
197 changes: 2 additions & 195 deletions scratch.dib
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#!csharp

#r "/Users/iankeough/dev/Hypar/Elements/Elements/src/bin/Debug/netstandard2.0/Hypar.Elements.dll"
#r "./Elements/src/bin/Debug/netstandard2.0/Hypar.Elements.dll"
#r "nuget:glTF2Loader, 1.1.3-alpha"
#r "nuget:Unofficial.LibTessDotNet, 2.0.0"
#r "nuget:SixLabors.Fonts, 1.0.0-beta19"
Expand All @@ -13,200 +13,7 @@

#!csharp

using System.CommandLine;
using System.CommandLine.Invocation;
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Formatting;
using Elements;
using Elements.Geometry;
using Elements.Serialization.glTF;
using Newtonsoft.Json;
using System;
using System.IO;

var viewerSrc = @"
<div id=""main_DIV_ID"" style=""height:WIDTH_VAR;width:HEIGHT_VARpx;""></div>
</div>
<script type=""module"">
import * as THREE from 'https://unpkg.com/[email protected]/build/three.module.js';
import { GLTFLoader } from 'https://unpkg.com/[email protected]/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js';
const loader = new GLTFLoader();
const scene = new THREE.Scene();

let loaderScene = new THREE.Scene();
scene.add(loaderScene);

const width = WIDTH_VAR;
const height = HEIGHT_VAR;
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
// TODO: Configure this lighting to match what we do on Hypar's web UI.
const light = new THREE.AmbientLight(0xaaaaaa);
scene.add(light);

const directionalLight = new THREE.DirectionalLight(0xaaaaaa, 0.5);
scene.add(directionalLight);


const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(width, height);
document.getElementById('main_DIV_ID').appendChild(renderer.domElement);

camera.position.z = 5;

const controls = new OrbitControls(camera, renderer.domElement);

function base64ToArrayBuffer (base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
// adapted from looeee's solution https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/3
const fitCameraToObject = function (scene, offset = 1.25) {
offset = offset || 1.25;
let boundingBox = null;
scene.traverseVisible((child) => {
const objectBox = new THREE.Box3().setFromObject(child)
if (boundingBox === null) {
boundingBox = objectBox
} else {
boundingBox = boundingBox.union(objectBox)
}
})
const center = boundingBox.getCenter(new THREE.Vector3());
const size = boundingBox.getSize(new THREE.Vector3());

// get the max size of the bounding box (fits to width OR height as needed )
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));
// if our model is nearly flat, view it from the top, otherwise view it from a corner.
if (size.y < 0.001) {
// position the camera looking straight down at the object.
camera.position.copy(center.clone().add(new THREE.Vector3(0, maxDim * offset, 0)))
} else {
camera.position.copy(center.clone().add(size.clone().multiplyScalar(offset)))
}

const minZ = boundingBox.min.z;
const cameraToFarEdge = (minZ < 0) ? -minZ + cameraZ : cameraZ - minZ;

camera.far = cameraToFarEdge * 10;
camera.updateProjectionMatrix();

if (controls) {
// set camera to rotate around center of loaded object
controls.target = center;
controls.saveState();

} else {
camera.lookAt(center)
}
}

function animate () {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
};

const modelBytes = ""MODEL_BYTES_HERE"";

const gltf = loader.parse(base64ToArrayBuffer(modelBytes), null, (glb) => {
loaderScene.add(glb.scene);
fitCameraToObject(scene);
});


animate();

</script>
";

if (KernelInvocationContext.Current is { } currentContext)
{
currentContext.DisplayAs("Add `return model;` at the end of a cell or call `DisplayModel(model, width, height);` to display an Elements model. You can also return individual elements, curves, profiles, or lists of elements, curves, or profiles to automatically populate a model.", "text/markdown");
}

double DEFAULT_MODEL_WIDTH = 600;
double DEFAULT_MODEL_HEIGHT = 400;
Material DEFAULT_CURVE_MATERIAL = BuiltInMaterials.XAxis;

string GetModelViewerSrc(Model model, double? width=null, double? height=null) {
var gltf = model.ToGlTF();
var gltfString = Convert.ToBase64String(gltf, 0, gltf.Length);
return viewerSrc
.Replace("MODEL_BYTES_HERE", gltfString)
.Replace("WIDTH_VAR", (width ?? DEFAULT_MODEL_WIDTH).ToString())
.Replace("HEIGHT_VAR", (height ?? DEFAULT_MODEL_HEIGHT).ToString())
.Replace("DIV_ID", Guid.NewGuid().ToString());
}

Formatter.Register<BoundedCurve>((crv, writer) => {
var model = new Model();
model.AddElement(new ModelCurve(crv, DEFAULT_CURVE_MATERIAL));
var src = GetModelViewerSrc(model);
writer.Write(src);

}, "text/html");

Formatter.Register<Profile>((p, writer) => {
var model = new Model();
model.AddElements(p.ToModelCurves(null, DEFAULT_CURVE_MATERIAL));
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<Element>((e, writer) => {
var model = new Model();
model.AddElement(e);
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<IEnumerable<Element>>((elements, writer) => {
var model = new Model();
model.AddElements(elements);
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<IEnumerable<BoundedCurve>>((crvs, writer) => {
var model = new Model();
model.AddElements(crvs.Select(crv => new ModelCurve(crv, DEFAULT_CURVE_MATERIAL)));
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<IEnumerable<Profile>>((profiles, writer) => {
var model = new Model();
model.AddElements(profiles.SelectMany(p => p.ToModelCurves(null, DEFAULT_CURVE_MATERIAL)));
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

Formatter.Register<Model>((model, writer) => {
var src = GetModelViewerSrc(model);
writer.Write(src);
}, "text/html");

void DisplayModel(Model model, double? width=null, double? height=null) {
var src = GetModelViewerSrc(model, width ?? DEFAULT_MODEL_WIDTH, height ?? DEFAULT_MODEL_HEIGHT);
KernelInvocationContext.Current.DisplayAs(src, "text/html");
}

void SetDefaultDisplaySize(double width, double height) {
DEFAULT_MODEL_WIDTH = width;
DEFAULT_MODEL_HEIGHT = height;
}

void SetDefaultCurveMaterial(Material mat) {
DEFAULT_CURVE_MATERIAL = mat;
}
#!import "./Elements/src/extension.dib"

#!csharp

Expand Down

0 comments on commit 213f4b7

Please sign in to comment.