ruby3mf 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/PO_102_03.3mf +0 -0
- data/bin/batch.rb +3 -5
- data/bin/cli.rb +1 -4
- data/foo/2D/ffffa2c3-ba74-4bea-a4d0-167a4211134d.model +18747 -0
- data/foo/3D/3dmodel.model +40 -0
- data/foo/3D/_rels/3dmodel.model.rels +4 -0
- data/foo/Thumbnails/ffffa6c3-ba74-4bea-a4d0-167a4211134d.model +0 -0
- data/foo/[Content_Types].xml +7 -0
- data/foo/_rels/.rels +5 -0
- data/lib/ruby3mf/3MFcoreSpec_1.1.xsd.template +2 -2
- data/lib/ruby3mf/content_types.rb +42 -18
- data/lib/ruby3mf/document.rb +19 -33
- data/lib/ruby3mf/edge_list.rb +3 -4
- data/lib/ruby3mf/errors.yml +15 -9
- data/lib/ruby3mf/log3mf.rb +22 -31
- data/lib/ruby3mf/mesh_analyzer.rb +35 -32
- data/lib/ruby3mf/model3mf.rb +12 -33
- data/lib/ruby3mf/schema_files.rb +16 -2
- data/lib/ruby3mf/thumbnail3mf.rb +5 -0
- data/lib/ruby3mf/version.rb +1 -1
- data/lib/ruby3mf/xml_val.rb +1 -8
- data/suite.011917.out +237 -0
- metadata +10 -3
- data/bin/suite.rb +0 -50
@@ -0,0 +1,40 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<model unit="millimeter" xml:lang="en-US" xmlns="http://schemas.microsoft.com/3dmanufacturing/core/2015/02" xmlns:s="http://schemas.microsoft.com/3dmanufacturing/slice/2015/07" requiredextensions="s p" xmlns:p="http://schemas.microsoft.com/3dmanufacturing/production/2015/06">
|
3
|
+
<resources>
|
4
|
+
<s:slicestack id="3" zbottom="0.000">
|
5
|
+
<s:sliceref slicestackid="1" slicepath="/2D/ffffa2c3-ba74-4bea-a4d0-167a4211134d.model" />
|
6
|
+
</s:slicestack>
|
7
|
+
|
8
|
+
<object id="2" name="S11_cube_NA_Sliced" s:meshresolution="lowres" s:slicestackid="3" p:UUID="ffffa2c3-ba74-4bea-a4d0-167a4211134d">
|
9
|
+
<mesh>
|
10
|
+
<vertices>
|
11
|
+
<vertex x="100.001" y="100.000" z="100.000" />
|
12
|
+
<vertex x="100.001" y="0.000" z="100.000" />
|
13
|
+
<vertex x="100.001" y="100.000" z="0.000" />
|
14
|
+
<vertex x="0.000" y="100.000" z="0.000" />
|
15
|
+
<vertex x="100.001" y="0.000" z="0.000" />
|
16
|
+
<vertex x="0.000" y="0.000" z="0.000" />
|
17
|
+
<vertex x="0.000" y="0.000" z="100.000" />
|
18
|
+
<vertex x="0.000" y="100.000" z="100.000" />
|
19
|
+
</vertices>
|
20
|
+
<triangles>
|
21
|
+
<triangle v1="0" v2="1" v3="2" />
|
22
|
+
<triangle v1="3" v2="0" v3="2" />
|
23
|
+
<triangle v1="4" v2="3" v3="2" />
|
24
|
+
<triangle v1="5" v2="3" v3="4" />
|
25
|
+
<triangle v1="4" v2="6" v3="5" />
|
26
|
+
<triangle v1="6" v2="7" v3="5" />
|
27
|
+
<triangle v1="7" v2="6" v3="0" />
|
28
|
+
<triangle v1="1" v2="6" v3="4" />
|
29
|
+
<triangle v1="5" v2="7" v3="3" />
|
30
|
+
<triangle v1="7" v2="0" v3="3" />
|
31
|
+
<triangle v1="2" v2="1" v3="4" />
|
32
|
+
<triangle v1="0" v2="6" v3="1" />
|
33
|
+
</triangles>
|
34
|
+
</mesh>
|
35
|
+
</object>
|
36
|
+
</resources>
|
37
|
+
<build p:UUID="ab2ef9d9-5cb2-414c-bfed-a29e29e1f977">
|
38
|
+
<item objectid="2" transform="1.0000 0.0000 0.0000 0.0000 1.0000 0.0000 0.0000 0.0000 1.0000 30.0990 35.1000 30.1000" p:UUID="e0ad3d02-a9f2-47e7-b84f-12c588837f5b"/>
|
39
|
+
</build>
|
40
|
+
</model>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<Relationship Target="/2D/ffffa2c3-ba74-4bea-a4d0-167a4211134d.model" Id="rel1" Type="http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"/>
|
4
|
+
</Relationships>
|
Binary file
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
3
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
|
4
|
+
<Default Extension="model" ContentType="application/vnd.ms-package.3dmanufacturing-3dmodel+xml" />
|
5
|
+
<Default Extension="png" ContentType="image/png" />
|
6
|
+
<Override PartName="/Thumbnails/ffffa6c3-ba74-4bea-a4d0-167a4211134d.model" ContentType="image/png" />
|
7
|
+
</Types>
|
data/foo/_rels/.rels
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
3
|
+
<Relationship Target="/3D/3dmodel.model" Id="rel0" Type="http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"/>
|
4
|
+
<Relationship Target="/Thumbnails/ffffa6c3-ba74-4bea-a4d0-167a4211134d.model" Id="rel2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"/>
|
5
|
+
</Relationships>
|
@@ -71,7 +71,7 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
71
71
|
<xs:sequence>
|
72
72
|
<xs:element ref="vertices"/>
|
73
73
|
<xs:element ref="triangles"/>
|
74
|
-
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="
|
74
|
+
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="30000"/>
|
75
75
|
</xs:sequence>
|
76
76
|
</xs:complexType>
|
77
77
|
<xs:complexType name="CT_Vertices">
|
@@ -87,7 +87,7 @@ Items within this schema follow a simple naming convention of appending a prefix
|
|
87
87
|
</xs:complexType>
|
88
88
|
<xs:complexType name="CT_Triangles">
|
89
89
|
<xs:sequence>
|
90
|
-
<xs:element ref="triangle" minOccurs="1" maxOccurs="
|
90
|
+
<xs:element ref="triangle" minOccurs="1" maxOccurs="30000"/>
|
91
91
|
</xs:sequence>
|
92
92
|
</xs:complexType>
|
93
93
|
<xs:complexType name="CT_Triangle">
|
@@ -1,36 +1,61 @@
|
|
1
1
|
class ContentTypes
|
2
2
|
|
3
|
-
def
|
4
|
-
found_types=
|
5
|
-
found_overrides=
|
3
|
+
def initialize(found={}, over={})
|
4
|
+
@found_types=found
|
5
|
+
@found_overrides=over
|
6
|
+
end
|
7
|
+
|
8
|
+
def size
|
9
|
+
@found_types.size + @found_overrides.size
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
size == 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_type(target)
|
17
|
+
target = (target.start_with?('/') ? target : '/' + target).downcase
|
18
|
+
if @found_overrides[target]
|
19
|
+
content_type = @found_overrides[target]
|
20
|
+
else
|
21
|
+
extension = File.extname(target).strip.downcase[1..-1]
|
22
|
+
content_type = @found_types[extension]
|
23
|
+
end
|
24
|
+
content_type
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_types()
|
28
|
+
return @found_types.values + @found_overrides.values
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
6
32
|
|
33
|
+
def self.parse(zip_entry)
|
34
|
+
found_types = {}
|
35
|
+
found_overrides = {}
|
7
36
|
Log3mf.context "parse" do |l|
|
8
37
|
begin
|
9
|
-
|
10
38
|
doc = XmlVal.validate_parse(zip_entry)
|
11
39
|
|
12
40
|
l.warning '[Content_Types].xml must contain exactly one root node' unless doc.children.size == 1
|
13
41
|
l.warning '[Content_Types].xml must contain root name Types' unless doc.children.first.name == "Types"
|
14
42
|
|
15
|
-
required_content_types = ['application/vnd.openxmlformats-package.relationships+xml'
|
16
|
-
#optional_content_types = ['application/vnd.ms-printing.printticket+xml']
|
17
|
-
#all_types = required_content_types + optional_content_types
|
43
|
+
required_content_types = ['application/vnd.openxmlformats-package.relationships+xml']
|
18
44
|
|
19
45
|
types_node = doc.children.first
|
20
46
|
types_node.children.each do |node|
|
21
47
|
l.context node.name do |l|
|
22
48
|
if node.name == 'Default'
|
23
|
-
|
24
|
-
|
25
|
-
l.
|
26
|
-
|
27
|
-
l.error :duplicate_content_extension_types if !found_types[node['Extension']].nil?
|
28
|
-
found_types[node['Extension']] = node['ContentType']
|
49
|
+
extension = node['Extension'].downcase
|
50
|
+
l.info "Setting type hash #{extension}=#{node['ContentType']}"
|
51
|
+
l.error :duplicate_content_extension_types if !found_types[extension].nil?
|
52
|
+
found_types[extension] = node['ContentType']
|
29
53
|
elsif node.name == 'Override'
|
30
|
-
|
54
|
+
part_name = node['PartName'].downcase
|
55
|
+
l.error :empty_override_part_name if part_name.empty?
|
31
56
|
|
32
|
-
l.error :duplicate_content_override_types if !found_overrides[
|
33
|
-
found_overrides[
|
57
|
+
l.error :duplicate_content_override_types if !found_overrides[part_name].nil?
|
58
|
+
found_overrides[part_name] = node['ContentType']
|
34
59
|
else
|
35
60
|
l.warning "[Content_Types].xml:#{node.line} contains unexpected element #{node.name}", page: 10
|
36
61
|
end
|
@@ -43,7 +68,6 @@ class ContentTypes
|
|
43
68
|
l.error "[Content_Types].xml file is not valid XML. #{e}", page: 15
|
44
69
|
end
|
45
70
|
end
|
46
|
-
|
47
|
-
found_types
|
71
|
+
return new(found_types, found_overrides)
|
48
72
|
end
|
49
73
|
end
|
data/lib/ruby3mf/document.rb
CHANGED
@@ -19,20 +19,12 @@ class Document
|
|
19
19
|
THUMBNAIL_TYPES = %w[image/jpeg image/png].freeze
|
20
20
|
TEXTURE_TYPES = %w[image/jpeg image/png application/vnd.ms-package.3dmanufacturing-3dmodeltexture].freeze
|
21
21
|
|
22
|
-
# Relationship to valid Content types
|
23
|
-
REL_TO_CONTENT_TYPES = {
|
24
|
-
MODEL_TYPE => 'application/vnd.ms-package.3dmanufacturing-3dmodel+xml',
|
25
|
-
PRINT_TICKET_TYPE => 'application/vnd.ms-printing.printticket+xml',
|
26
|
-
TEXTURE_TYPE => TEXTURE_TYPES,
|
27
|
-
THUMBNAIL_TYPE => THUMBNAIL_TYPES
|
28
|
-
}
|
29
|
-
|
30
22
|
# Relationship Type => Class validating relationship type
|
31
23
|
RELATIONSHIP_TYPES = {
|
32
|
-
MODEL_TYPE => {klass: 'Model3mf', collection: :models},
|
33
|
-
THUMBNAIL_TYPE => {klass: 'Thumbnail3mf', collection: :thumbnails},
|
34
|
-
TEXTURE_TYPE => {klass: 'Texture3mf', collection: :textures},
|
35
|
-
PRINT_TICKET_TYPE => {}
|
24
|
+
MODEL_TYPE => {klass: 'Model3mf', collection: :models, valid_types: ['application/vnd.ms-package.3dmanufacturing-3dmodel+xml']},
|
25
|
+
THUMBNAIL_TYPE => {klass: 'Thumbnail3mf', collection: :thumbnails, valid_types: THUMBNAIL_TYPES},
|
26
|
+
TEXTURE_TYPE => {klass: 'Texture3mf', collection: :textures, valid_types: TEXTURE_TYPES},
|
27
|
+
PRINT_TICKET_TYPE => {valid_types: ['application/vnd.ms-printing.printticket+xml']}
|
36
28
|
}
|
37
29
|
|
38
30
|
def initialize(zip_filename)
|
@@ -41,7 +33,7 @@ class Document
|
|
41
33
|
self.textures=[]
|
42
34
|
self.objects={}
|
43
35
|
self.relationships={}
|
44
|
-
self.types=
|
36
|
+
self.types=nil
|
45
37
|
self.parts=[]
|
46
38
|
@zip_filename = zip_filename
|
47
39
|
end
|
@@ -49,7 +41,7 @@ class Document
|
|
49
41
|
#verify that each texture part in the 3MF is related to the model through a texture relationship in a rels file
|
50
42
|
def self.validate_texture_parts(document, log)
|
51
43
|
unless document.types.empty?
|
52
|
-
document.parts.select { |part| TEXTURE_TYPES.include?(document.types
|
44
|
+
document.parts.select { |part| TEXTURE_TYPES.include?(document.types.get_type(part)) }.each do |tfile|
|
53
45
|
if document.textures.select { |f| f[:target] == tfile }.size == 0
|
54
46
|
if document.thumbnails.select { |t| t[:target] == tfile }.size == 0
|
55
47
|
log.context "part names" do |l|
|
@@ -105,10 +97,6 @@ class Document
|
|
105
97
|
content_type_match = zip_file.glob('\[Content_Types\].xml').first
|
106
98
|
if content_type_match
|
107
99
|
m.types = ContentTypes.parse(content_type_match)
|
108
|
-
model_extension = m.types.key('application/vnd.ms-package.3dmanufacturing-3dmodel+xml')
|
109
|
-
model_extension = model_extension.downcase unless model_extension.nil?
|
110
|
-
model_file = zip_file.glob("**/*.#{model_extension}").first
|
111
|
-
l.error :no_3d_model, extension: model_extension if model_file.nil?
|
112
100
|
else
|
113
101
|
l.fatal_error 'Missing required file: [Content_Types].xml', page: 4
|
114
102
|
end
|
@@ -146,26 +134,24 @@ class Document
|
|
146
134
|
l.error :err_uri_empty_segment if target.end_with? '/' or target.include? '//'
|
147
135
|
l.error :err_uri_relative_path if target.include? '/../'
|
148
136
|
relationship_file = zip_file.glob(target).first
|
149
|
-
|
150
|
-
# check that relationships are valid; extensions and relationship types must jive
|
151
|
-
extension = File.extname(target).strip.downcase[1..-1]
|
152
|
-
|
153
|
-
content_type = m.types[extension]
|
154
137
|
rel_type = rel[:type]
|
155
|
-
expected_content_type = REL_TO_CONTENT_TYPES[rel_type]
|
156
|
-
|
157
|
-
if (expected_content_type)
|
158
|
-
l.error :missing_extension_in_content_types, ext: extension unless content_type
|
159
|
-
l.error :resource_contentype_invalid, bt: content_type, rt: rel[:target] unless (!content_type.nil? && expected_content_type.include?(content_type))
|
160
|
-
else
|
161
|
-
l.info "found unrecognized relationship type: #{rel_type}"
|
162
|
-
end
|
163
138
|
|
164
139
|
if relationship_file
|
165
|
-
relationship_type = RELATIONSHIP_TYPES[
|
140
|
+
relationship_type = RELATIONSHIP_TYPES[rel_type]
|
166
141
|
if relationship_type.nil?
|
167
|
-
l.
|
142
|
+
l.warning :unsupported_relationship_type, type: rel[:type], target: rel[:target]
|
168
143
|
else
|
144
|
+
# check that relationships are valid; extensions and relationship types must jive
|
145
|
+
content_type = m.types.get_type(target)
|
146
|
+
expected_content_type = relationship_type[:valid_types]
|
147
|
+
|
148
|
+
if (expected_content_type)
|
149
|
+
l.error :missing_content_type, part: target unless content_type
|
150
|
+
l.error :resource_contentype_invalid, bt: content_type, rt: rel[:target] unless (content_type.nil? || expected_content_type.include?(content_type))
|
151
|
+
else
|
152
|
+
l.info "found unrecognized relationship type: #{rel_type}"
|
153
|
+
end
|
154
|
+
|
169
155
|
unless relationship_type[:klass].nil?
|
170
156
|
m.send(relationship_type[:collection]) << {
|
171
157
|
rel_id: rel[:id],
|
data/lib/ruby3mf/edge_list.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class EdgeList
|
2
2
|
|
3
|
-
def initialize
|
3
|
+
def initialize
|
4
4
|
@edges = { }
|
5
5
|
end
|
6
6
|
|
@@ -34,14 +34,14 @@ class EdgeList
|
|
34
34
|
@edges[edge]
|
35
35
|
end
|
36
36
|
|
37
|
-
def print_list
|
37
|
+
def print_list
|
38
38
|
@edges.each do |key, value|
|
39
39
|
(pos, neg) = value
|
40
40
|
puts "#{pos} : #{neg}"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def verify_edges
|
44
|
+
def verify_edges
|
45
45
|
@edges.each do |key, value|
|
46
46
|
(pos, neg) = value
|
47
47
|
|
@@ -58,4 +58,3 @@ class EdgeList
|
|
58
58
|
end
|
59
59
|
|
60
60
|
end
|
61
|
-
|
data/lib/ruby3mf/errors.yml
CHANGED
@@ -47,13 +47,13 @@ invalid_content_type:
|
|
47
47
|
msg: "[Content_Types].xml is missing required ContentType \"application/vnd.openxmlformats-package.relationships+xml\""
|
48
48
|
page: 10
|
49
49
|
invalid_startpart_type:
|
50
|
-
msg: "rels/.rels Relationship file
|
50
|
+
msg: "rels/.rels Relationship file is missing a required StartPart relationship to the primary 3D payload"
|
51
51
|
page: 10
|
52
52
|
invalid_startpart_target:
|
53
53
|
msg: "Invalid StartPart target '%{target}'. The 3MF Document StartPart relationship MUST point to the 3D Model part that identifies the root of the 3D payload."
|
54
54
|
page: 10
|
55
|
-
|
56
|
-
msg: "
|
55
|
+
unsupported_relationship_type:
|
56
|
+
msg: "Validation of relationship type '%{type}' is not supported by this tool. The targeted part '%{target}' will not be validated."
|
57
57
|
page: 10
|
58
58
|
invalid_thumbnail_file:
|
59
59
|
msg: "thumbnail file must be valid image file"
|
@@ -70,8 +70,8 @@ invalid_texture_file_type:
|
|
70
70
|
missing_content_types:
|
71
71
|
msg: "Missing required file: [Content_Types].xml"
|
72
72
|
page: 4
|
73
|
-
|
74
|
-
msg: "
|
73
|
+
missing_content_type:
|
74
|
+
msg: "Unable to find an associated content type for part '%{part}' in [Content_Types].xml"
|
75
75
|
page: 10
|
76
76
|
missing_dot_rels_file:
|
77
77
|
msg: "Missing required file _rels/.rels"
|
@@ -86,9 +86,6 @@ non_unique_rel_id:
|
|
86
86
|
multiple_relationships:
|
87
87
|
msg: "There MUST NOT be more than one relationship of a given relationship type from one part to a second part"
|
88
88
|
page: 10
|
89
|
-
no_3d_model:
|
90
|
-
msg: "Required 3D model payload not found with provided Content Type model extension: .%{extension}"
|
91
|
-
page: 10
|
92
89
|
not_a_zip:
|
93
90
|
msg: "File provided is not a valid ZIP archive"
|
94
91
|
page: 9
|
@@ -166,4 +163,13 @@ duplicate_content_override_types:
|
|
166
163
|
page: 8
|
167
164
|
empty_override_part_name:
|
168
165
|
msg: "Overrides can't have empty partname"
|
169
|
-
page: 8
|
166
|
+
page: 8
|
167
|
+
not_enough_triangles:
|
168
|
+
msg: "Mesh has fewer than four triangles"
|
169
|
+
page: 30
|
170
|
+
has_base_materials_gradient:
|
171
|
+
msg: "Base materials form a gradient on one or more triangles. Interpolation of materials is not supported in the core spec."
|
172
|
+
page: 31
|
173
|
+
thumbnail_image_type_mismatch:
|
174
|
+
msg: "Image not of declared type"
|
175
|
+
page: 36
|
data/lib/ruby3mf/log3mf.rb
CHANGED
@@ -27,15 +27,24 @@ class Log3mf
|
|
27
27
|
|
28
28
|
LOG_LEVELS = [:fatal_error, :error, :warning, :info, :debug]
|
29
29
|
|
30
|
+
SPEC_LINKS = {
|
31
|
+
core: 'http://3mf.io/wp-content/uploads/2016/03/3MFcoreSpec_1.1.pdf',
|
32
|
+
material: 'http://3mf.io/wp-content/uploads/2015/04/3MFmaterialsSpec_1.0.1.pdf',
|
33
|
+
production: 'http://3mf.io/wp-content/uploads/2016/07/3MFproductionSpec.pdf',
|
34
|
+
slice: 'http://3mf.io/wp-content/uploads/2016/07/3MFsliceSpec.pdf',
|
35
|
+
#opc: 'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-376,%20Fourth%20Edition,%20Part%202%20-%20Open%20Packaging%20Conventions.zip'
|
36
|
+
opc: 'http://3mf.io/wp-content/uploads/2016/03/3MFcoreSpec_1.1.pdf'
|
37
|
+
}.freeze
|
38
|
+
|
30
39
|
# Allows us to throw FatalErrors if we ever get errors of severity :fatal_error
|
31
40
|
class FatalError < RuntimeError
|
32
41
|
end
|
33
42
|
|
34
|
-
def initialize
|
43
|
+
def initialize
|
35
44
|
@log_list = []
|
36
45
|
@context_stack = []
|
37
46
|
@ledger = []
|
38
|
-
errormap_path = File.join(File.dirname(__FILE__),"errors.yml")
|
47
|
+
errormap_path = File.join(File.dirname(__FILE__), "errors.yml")
|
39
48
|
@errormap = YAML.load_file(errormap_path)
|
40
49
|
end
|
41
50
|
|
@@ -50,10 +59,7 @@ class Log3mf
|
|
50
59
|
|
51
60
|
def context (context_description, &block)
|
52
61
|
@context_stack.push(context_description)
|
53
|
-
#puts "started context #{@context_stack.join("/")}"
|
54
|
-
|
55
62
|
retval = block.call(Log3mf.instance)
|
56
|
-
|
57
63
|
@context_stack.pop
|
58
64
|
retval
|
59
65
|
end
|
@@ -71,11 +77,15 @@ class Log3mf
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def log(severity, message, options = {})
|
74
|
-
error = @errormap.fetch(message.to_s) { {"msg" => message.to_s, "page" => nil
|
80
|
+
error = @errormap.fetch(message.to_s) { {"msg" => message.to_s, "page" => nil} }
|
75
81
|
options[:page] = error["page"] unless options[:page]
|
76
82
|
options[:spec] = error["spec"] unless options[:spec]
|
77
|
-
|
78
|
-
|
83
|
+
entry = { id: message,
|
84
|
+
context: "#{@context_stack.join("/")}",
|
85
|
+
severity: severity,
|
86
|
+
message: interpolate(error["msg"], options) }
|
87
|
+
entry[:spec_ref] = spec_link(options[:spec], options[:page]) if (options && options[:page])
|
88
|
+
@log_list << entry
|
79
89
|
raise FatalError if severity == :fatal_error
|
80
90
|
end
|
81
91
|
|
@@ -88,7 +98,8 @@ class Log3mf
|
|
88
98
|
end
|
89
99
|
|
90
100
|
def entries(*levels)
|
91
|
-
@log_list
|
101
|
+
return @log_list if levels.size == 0
|
102
|
+
@log_list.select { |i| levels.include? i[:severity] }
|
92
103
|
end
|
93
104
|
|
94
105
|
def self.entries(*l)
|
@@ -97,31 +108,11 @@ class Log3mf
|
|
97
108
|
|
98
109
|
def spec_link(spec, page)
|
99
110
|
spec = :core unless spec
|
100
|
-
|
101
|
-
core: 'http://3mf.io/wp-content/uploads/2016/03/3MFcoreSpec_1.1.pdf',
|
102
|
-
material: 'http://3mf.io/wp-content/uploads/2015/04/3MFmaterialsSpec_1.0.1.pdf',
|
103
|
-
production: 'http://3mf.io/wp-content/uploads/2016/07/3MFproductionSpec.pdf',
|
104
|
-
slice: 'http://3mf.io/wp-content/uploads/2016/07/3MFsliceSpec.pdf',
|
105
|
-
#opc: 'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-376,%20Fourth%20Edition,%20Part%202%20-%20Open%20Packaging%20Conventions.zip'
|
106
|
-
opc: 'http://3mf.io/wp-content/uploads/2016/03/3MFcoreSpec_1.1.pdf'
|
107
|
-
}
|
108
|
-
"#{doc_urls[spec]}#page=#{page}"
|
109
|
-
end
|
110
|
-
|
111
|
-
def to_hash
|
112
|
-
@log_list.collect { |ent|
|
113
|
-
h = { context: ent[0], severity: ent[1], message: ent[2] }
|
114
|
-
h[:spec_ref] = spec_link(ent[3][:spec], ent[3][:page]) if (ent[3] && ent[3][:page])
|
115
|
-
h
|
116
|
-
}
|
117
|
-
end
|
118
|
-
|
119
|
-
def self.to_hash
|
120
|
-
Log3mf.instance.to_hash
|
111
|
+
"#{SPEC_LINKS[spec]}#page=#{page}"
|
121
112
|
end
|
122
113
|
|
123
114
|
def to_json
|
124
|
-
|
115
|
+
@log_list.to_json
|
125
116
|
end
|
126
117
|
|
127
118
|
def self.to_json
|
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
class MeshAnalyzer
|
13
13
|
|
14
|
-
def self.validate_object(object)
|
14
|
+
def self.validate_object(object, includes_material)
|
15
15
|
Log3mf.context "verifying object" do |l|
|
16
16
|
children = object.children.map { |child| child.name }
|
17
17
|
have_override = object.attributes["pid"] or object.attributes["pindex"]
|
@@ -24,15 +24,30 @@ class MeshAnalyzer
|
|
24
24
|
# if a triangle has a pid, then the object needs a pid
|
25
25
|
has_triangle_pid = false
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
meshs = object.css('mesh')
|
28
|
+
meshs.each do |mesh|
|
29
|
+
|
30
|
+
triangles = mesh.css("triangle")
|
31
|
+
l.error :not_enough_triangles if triangles.count < 4
|
30
32
|
|
31
33
|
if triangles
|
32
|
-
triangles.
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
triangles.each do |triangle|
|
35
|
+
|
36
|
+
v1 = triangle.attributes["v1"].to_s.to_i
|
37
|
+
v2 = triangle.attributes["v2"].to_s.to_i
|
38
|
+
v3 = triangle.attributes["v3"].to_s.to_i
|
39
|
+
|
40
|
+
unless includes_material
|
41
|
+
l.context "validating property overrides" do |l|
|
42
|
+
property_overrides = []
|
43
|
+
property_overrides << triangle.attributes['p1'].to_s.to_i if triangle.attributes['p1']
|
44
|
+
property_overrides << triangle.attributes['p2'].to_s.to_i if triangle.attributes['p2']
|
45
|
+
property_overrides << triangle.attributes['p3'].to_s.to_i if triangle.attributes['p3']
|
46
|
+
|
47
|
+
property_overrides.reject! { |prop| prop.nil? }
|
48
|
+
l.error :has_base_materials_gradient unless property_overrides.uniq.size <= 1
|
49
|
+
end
|
50
|
+
end
|
36
51
|
|
37
52
|
if v1 == v2 || v2 == v3 || v3 == v1
|
38
53
|
l.error :non_distinct_indices
|
@@ -41,45 +56,33 @@ class MeshAnalyzer
|
|
41
56
|
list.add_edge(v1, v2)
|
42
57
|
list.add_edge(v2, v3)
|
43
58
|
list.add_edge(v3, v1)
|
44
|
-
|
45
|
-
if not has_triangle_pid
|
59
|
+
unless has_triangle_pid
|
46
60
|
has_triangle_pid = triangle.attributes["pid"] != nil
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
50
|
-
|
51
|
-
if has_triangle_pid and not has_object_material
|
64
|
+
if has_triangle_pid && !(object.attributes["pindex"] && object.attributes["pid"])
|
52
65
|
l.error :missing_object_pid
|
53
66
|
end
|
54
67
|
|
55
|
-
result = list.verify_edges
|
68
|
+
result = list.verify_edges
|
56
69
|
if result == :bad_orientation
|
57
|
-
l.
|
70
|
+
l.error :resource_3dmodel_orientation
|
58
71
|
elsif result == :hole
|
59
|
-
l.
|
72
|
+
l.error :resource_3dmodel_hole
|
60
73
|
elsif result == :nonmanifold
|
61
|
-
l.
|
74
|
+
l.error :resource_3dmodel_nonmanifold
|
62
75
|
end
|
76
|
+
|
63
77
|
end
|
64
78
|
end
|
65
79
|
end
|
66
80
|
end
|
67
81
|
|
68
|
-
def self.validate(model_doc)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
if node.name == "model"
|
73
|
-
resources = find_child(node, "resources")
|
74
|
-
|
75
|
-
if resources
|
76
|
-
resources.children.each do |resource|
|
77
|
-
solid_model = resource.attributes["type"].to_s() == "model" or resource.attributes["type"].to_s() == "solidsupport"
|
78
|
-
if resource.name == "object" and solid_model
|
79
|
-
validate_object(resource)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
82
|
+
def self.validate(model_doc, includes_material)
|
83
|
+
model_doc.css('model/resources/object').select { |object| ['model', 'solidsupport', ''].include?(object.attributes['type'].to_s) }.each do |object|
|
84
|
+
validate_object(object, includes_material)
|
83
85
|
end
|
84
86
|
end
|
85
|
-
|
87
|
+
|
88
|
+
end
|