Create a gist now

Instantly share code, notes, and snippets.

THREE = require('../lib/three')
CSG = require('../lib/csg')
ThreeBSP = require('../lib/threecsg')
toSTL = require('./to_stl')
fs = require('fs')
eachGrunnkrets = require('./each_grunnkrets')
incomes = require('./incomes')
ClipperLib = require('../lib/clipper_lib')
clipperScale = 1.0
# Convert coordinates from a GeoJson MultiPolygon to a ClipperLib polygon set
multiPolygonToClipperPolygons = (coordinates) ->
result = []
for group in coordinates
for polygon in group
p = []
for point in polygon
p.push({X: point[0]*clipperScale, Y: point[1]*clipperScale})
# ClipperLib polygon set to GeoJson MultiPolygon coordinates
clipperPolygonsToMultiPolygon = (polygons) ->
result = []
for polygon in polygons
p = []
for point in polygon
p.push([point.X/clipperScale, point.Y/clipperScale])
offsetMutiPolygon = (coordinates, offset) ->
# This code is mostly copied and pasted from
polygons = multiPolygonToClipperPolygons(coordinates)
cpr = new ClipperLib.Clipper()
polygons = cpr.SimplifyPolygons(polygons, ClipperLib.PolyFillType.pftNonZero);
polygons = ClipperLib.Clean(polygons, 50*clipperScale);
joinType = ClipperLib.JoinType.jtSquare
miterLimit = 1
autoFix = true
result = cpr.OffsetPolygons(polygons, offset, joinType, miterLimit, autoFix)
pathFromMultiPolygon = (coordinates, offset = null) ->
coordinates = offsetMutiPolygon(coordinates, offset*clipperScale) if offset?
path = new THREE.Path()
for group in coordinates
for polygon in group
firstPoint = true
for point in polygon
if firstPoint
path.moveTo(point[0], point[1])
firstPoint = false
path.lineTo(point[0], point[1])
return path
# Represents the geometry of a single grunnkrets
class Grunnkrets
constructor: (@record, @height, @wallThickness = null) ->
@gid = @record.gid
@geometry = null
getGeometry: ->
return @geometry if @geometry?
if @height < 50
return new THREE.Geometry()
outerShapes = pathFromMultiPolygon(@record.geo.coordinates).toShapes()
# It there were no shapes, just return an empty geometry
unless outerShapes.length > 0
return new THREE.Geometry()
# Extrude the outer shell
outerShell = new THREE.ExtrudeGeometry(outerShapes, {amount: @height, bevelEnabled: false})
# If we have not been asked to carve an insode, or if this isn't high enough to warrant a carve, return
# Also carving shapes with holes in them does not always work, so we just won't even try.
unless @wallThickness? && @wallThickness != 0 && @height > @wallThickness
return @geometry = outerShell
# Create an inner shape by offsetting the shape negatively
innerShapes = pathFromMultiPolygon(@record.geo.coordinates, -@wallThickness).toShapes()
console.log innerShapes.length
# If no shape was generated, this region is too small to carve
unless innerShapes.length > 0 # There was no interior to remove
console.log "There was no inner shell"
return @geometry = outerShell
# Extrude the inner shell
innerShell = new THREE.ExtrudeGeometry(innerShapes, {amount: @height , bevelEnabled: false})
# Translate the inner shell down by @height to leave a cap on the top and cut a hole on the bottom
for vertex in innerShell.vertices
vertex.z -= @wallThickness
# Subtract the innerShell from the outerShell and create the resulting THREE.Geometry
@geometry = (new ThreeBSP(outerShell)).subtract(new ThreeBSP(innerShell)).toGeometry()
# Return it
return @geometry
municipality: ->
build = ->
eachGrunnkrets (record) ->
if true || record.gid.slice(0,4) == '0301'
console.log "Processing region #{record.gid}"
wallThickness = 43.75
# if record.gid == '02191306' # Hack to get around generating a malformed mesh due to an unidentified bug
# wallThickness = null
grunnkrets = new Grunnkrets(record, incomes[record.gid]*0.005, wallThickness)
geometry = grunnkrets.getGeometry()
if geometry.faces.length > 0
fs.appendFile(__dirname+"/../output/_#{grunnkrets.gid.slice(0,4)}.stl", toSTL(geometry))
module.exports = build
# console.log "Generating geometry"
# geometry = new THREE.SphereGeometry(100.0)
# console.log "Making STL"
# fs.writeFile("./model.stl", toSTL(geometry))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment