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 %} |