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.
@@ -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>
@@ -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>
@@ -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="unbounded"/>
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="unbounded"/>
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 self.parse(zip_entry)
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', 'application/vnd.ms-package.3dmanufacturing-3dmodel+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
- # l.error "[Content_Types].xml:#{node.line} contains Default node without defined Extension attribute" unless node['Extension'].is_a? String
24
- # l.error "[Content_Types].xml:#{node.line} contains Default node with unexpected ContentType \"#{node['ContentType']}\"", page: 10 unless all_types.include? node['ContentType']
25
- l.info "Setting type hash #{node['Extension']}=#{node['ContentType']}"
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
- l.error :empty_override_part_name if node['PartName'].empty?
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[node['PartName']].nil?
33
- found_overrides[node['PartName']] = node['ContentType']
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
@@ -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[File.extname(part).strip.downcase[1..-1]]) }.each do |tfile|
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[rel[:type]]
140
+ relationship_type = RELATIONSHIP_TYPES[rel_type]
166
141
  if relationship_type.nil?
167
- l.error :invalid_relationship_type, type: rel[:type]
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],
@@ -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
-
@@ -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 has an invalid attribute type for the root 3D Model (StartPart). The required type is \"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\""
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
- invalid_relationship_type:
56
- msg: "Invalid relationship type '%{type}' specified in .rels relationship file. Parts in the 3D payload MUST use one of the appropriate relationship types to establish that relationship between two parts in the payload."
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
- missing_extension_in_content_types:
74
- msg: "Missing extension '%{ext}' in [Content_Types].xml"
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
@@ -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
- message = interpolate(error["msg"], options)
78
- @log_list << ["#{@context_stack.join("/")}", severity, message, options] unless severity==:debug && ENV['LOGDEBUG'].nil?
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.select { |i| levels.include? i[1] }
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
- doc_urls={
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
- to_hash.to_json
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
- mesh = find_child(object, "mesh")
28
- if mesh
29
- triangles = find_child(mesh, "triangles")
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.children.each do |triangle|
33
- v1 = triangle.attributes["v1"].to_s().to_i()
34
- v2 = triangle.attributes["v2"].to_s().to_i()
35
- v3 = triangle.attributes["v3"].to_s().to_i()
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
- has_object_material = object.attributes["pid"] and object.attributes["pindex"]
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.fatal_error :resource_3dmodel_orientation
70
+ l.error :resource_3dmodel_orientation
58
71
  elsif result == :hole
59
- l.fatal_error :resource_3dmodel_hole
72
+ l.error :resource_3dmodel_hole
60
73
  elsif result == :nonmanifold
61
- l.fatal_error :resource_3dmodel_nonmanifold
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
- root = model_doc.root
70
- node = root
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
- end
87
+
88
+ end