From 72fec22d4b22b7a5b1b953393d183b1236417a50 Mon Sep 17 00:00:00 2001
From: Rodrigo Rodrigues da Silva <rsilva@metamaquina.com.br>
Date: Mon, 9 Jun 2014 11:32:11 -0300
Subject: [PATCH] Add (optional) volume calculation to STL files.
---
mediagoblin/media_types/stl/migrations.py | 14 +++++++
mediagoblin/media_types/stl/model_loader.py | 43 +++++++++++++++++---
mediagoblin/media_types/stl/models.py | 1 +
mediagoblin/media_types/stl/processing.py | 1 +
.../templates/mediagoblin/media_displays/stl.html | 4 ++
5 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/mediagoblin/media_types/stl/migrations.py b/mediagoblin/media_types/stl/migrations.py
index f54c23e..64edca1 100644
|
a
|
b
|
|
| 14 | 14 | # You should have received a copy of the GNU Affero General Public License |
| 15 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | 16 | |
| | 17 | from sqlalchemy import MetaData, Table, Column, Float |
| | 18 | from mediagoblin.db.migration_tools import RegisterMigration |
| | 19 | |
| 17 | 20 | MIGRATIONS = {} |
| | 21 | |
| | 22 | @RegisterMigration(1, MIGRATIONS) |
| | 23 | def add_volume_column(db_conn): |
| | 24 | metadata = MetaData(bind=db_conn.bind) |
| | 25 | |
| | 26 | stl_data = Table('stl__mediadata', metadata, autoload=True, |
| | 27 | autoload_with=db_conn.bind) |
| | 28 | |
| | 29 | col = Column('volume', Float, nullable=True) |
| | 30 | col.create(stl_data) |
| | 31 | db_conn.commit() |
diff --git a/mediagoblin/media_types/stl/model_loader.py b/mediagoblin/media_types/stl/model_loader.py
index 88f1931..14f0ba1 100644
|
a
|
b
|
|
| 1 | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
| 2 | | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
| | 2 | # Copyright (C) 2011, 2012, 2014 MediaGoblin contributors. See AUTHORS. |
| 3 | 3 | # |
| 4 | 4 | # This program is free software: you can redistribute it and/or modify |
| 5 | 5 | # it under the terms of the GNU Affero General Public License as published by |
| … |
… |
|
| 16 | 16 | |
| 17 | 17 | |
| 18 | 18 | import struct |
| | 19 | import logging |
| 19 | 20 | |
| | 21 | _use_admesh = True |
| | 22 | try: |
| | 23 | import admesh |
| | 24 | except: |
| | 25 | _use_admesh = False |
| | 26 | |
| | 27 | _log = logging.getLogger(__name__) |
| 20 | 28 | |
| 21 | 29 | class ThreeDeeParseError(Exception): |
| 22 | 30 | pass |
| 23 | 31 | |
| 24 | 32 | |
| 25 | | class ThreeDee(): |
| | 33 | class ThreeDee(object): |
| 26 | 34 | """ |
| 27 | 35 | 3D model parser base class. Derrived classes are used for basic |
| 28 | 36 | analysis of 3D models, and are not intended to be used for 3D |
| … |
… |
class ThreeDee():
|
| 37 | 45 | self.width = 0 # x axis |
| 38 | 46 | self.depth = 0 # y axis |
| 39 | 47 | self.height = 0 # z axis |
| | 48 | self.volume = None |
| 40 | 49 | |
| 41 | 50 | self.load(fileob) |
| 42 | 51 | if not len(self.verts): |
| … |
… |
class ObjModel(ThreeDee):
|
| 83 | 92 | line = line.strip() |
| 84 | 93 | if line[0] == "v": |
| 85 | 94 | self.verts.append(self.__vector(line)) |
| 86 | | |
| 87 | 95 | |
| 88 | | class BinaryStlModel(ThreeDee): |
| | 96 | class StlModel(ObjModel): |
| | 97 | """ |
| | 98 | General parser for STL models. This class provides a wrapper |
| | 99 | method to use admesh to calculate STL file volume (admesh can't |
| | 100 | parse OBJ files) |
| | 101 | """ |
| | 102 | |
| | 103 | def __volume(self, fileob): |
| | 104 | if _use_admesh: |
| | 105 | stl_file = admesh.stl_file() |
| | 106 | admesh.stl_open(stl_file, str(fileob.name)) |
| | 107 | admesh.stl_calculate_volume(stl_file) |
| | 108 | self.volume = stl_file.stats.volume |
| | 109 | |
| | 110 | def __init__(self, fileob): |
| | 111 | super(StlModel, self).__init__(fileob) |
| | 112 | if _use_admesh: |
| | 113 | _log.info("Using admesh to calculate volume") |
| | 114 | self.__volume(fileob) |
| | 115 | else: |
| | 116 | _log.warn("Admesh not installed. Bypassing volume calculation. ") |
| | 117 | |
| | 118 | def load(self, fileob): |
| | 119 | super(ObjModel,self).load(fileob) |
| | 120 | |
| | 121 | class BinaryStlModel(StlModel): |
| 89 | 122 | """ |
| 90 | 123 | Parser for ascii-encoded stl files. File format reference: |
| 91 | 124 | http://en.wikipedia.org/wiki/STL_%28file_format%29#Binary_STL |
| … |
… |
def auto_detect(fileob, hint):
|
| 117 | 150 | # HACK Ascii formatted stls are similar enough to obj |
| 118 | 151 | # files that we can just use the same parser for both. |
| 119 | 152 | # Isn't that something? |
| 120 | | return ObjModel(fileob) |
| | 153 | return StlModel(fileob) |
| 121 | 154 | except ThreeDeeParseError: |
| 122 | 155 | pass |
| 123 | 156 | except ValueError: |
diff --git a/mediagoblin/media_types/stl/models.py b/mediagoblin/media_types/stl/models.py
index ff50e9c..c8d4c2f 100644
|
a
|
b
|
class StlData(Base):
|
| 42 | 42 | width = Column(Float) |
| 43 | 43 | height = Column(Float) |
| 44 | 44 | depth = Column(Float) |
| | 45 | volume = Column(Float) |
| 45 | 46 | |
| 46 | 47 | file_type = Column(String) |
| 47 | 48 | |
diff --git a/mediagoblin/media_types/stl/processing.py b/mediagoblin/media_types/stl/processing.py
index 65a8623..d36d42a 100644
|
a
|
b
|
class CommonStlProcessor(MediaProcessor):
|
| 253 | 253 | "width": self.model.width, |
| 254 | 254 | "height": self.model.height, |
| 255 | 255 | "depth": self.model.depth, |
| | 256 | "volume": self.model.volume, |
| 256 | 257 | "file_type": self.ext, |
| 257 | 258 | } |
| 258 | 259 | self.entry.media_data_init(**dimensions) |
diff --git a/mediagoblin/templates/mediagoblin/media_displays/stl.html b/mediagoblin/templates/mediagoblin/media_displays/stl.html
index ca0479c..85f50a4 100644
|
a
|
b
|
window.show_things = function () {
|
| 146 | 146 | <p>{{ media.media_data.file_type }}</p> |
| 147 | 147 | <h3>{% trans %}Object Height{% endtrans %}</h3> |
| 148 | 148 | <p>~{{ media.media_data.height|int }} mm</p> |
| | 149 | {% if media.media_data.volume %} |
| | 150 | <h3>{% trans %}Object Volume{% endtrans %}</h3> |
| | 151 | <p>~{{ media.media_data.volume|int }} mm³</p> |
| | 152 | {% endif %} |
| 149 | 153 | {% endblock %} |