lithop 0.1.0
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 +7 -0
- data/CHANGELOG.md +8 -0
- data/LICENSE +21 -0
- data/README.md +126 -0
- data/RELEASING.md +63 -0
- data/bin/lithop +6 -0
- data/docs/README.md +44 -0
- data/docs/artifacts.md +122 -0
- data/docs/bambu-studio.md +93 -0
- data/docs/calibration.md +126 -0
- data/docs/color-model.md +114 -0
- data/docs/command.md +182 -0
- data/docs/config.md +201 -0
- data/docs/getting-started.md +98 -0
- data/docs/troubleshooting.md +112 -0
- data/lib/lithop/calibration/export.rb +102 -0
- data/lib/lithop/calibration/profile.rb +127 -0
- data/lib/lithop/calibration/profile_measurements.rb +82 -0
- data/lib/lithop/calibration/recipe_selector.rb +54 -0
- data/lib/lithop/calibration/target.rb +135 -0
- data/lib/lithop/calibration/target_svg.rb +89 -0
- data/lib/lithop/calibration/thickness_selector.rb +35 -0
- data/lib/lithop/cli/calibration.rb +40 -0
- data/lib/lithop/cli/image_options.rb +69 -0
- data/lib/lithop/cli/options.rb +95 -0
- data/lib/lithop/cli/settings.rb +34 -0
- data/lib/lithop/cli.rb +102 -0
- data/lib/lithop/export/calibration.rb +46 -0
- data/lib/lithop/export/color_metadata.rb +80 -0
- data/lib/lithop/export/generation.rb +122 -0
- data/lib/lithop/export/job.rb +49 -0
- data/lib/lithop/export/metadata.rb +85 -0
- data/lib/lithop/export/planning.rb +78 -0
- data/lib/lithop/export/previews.rb +35 -0
- data/lib/lithop/export/print_readme.rb +69 -0
- data/lib/lithop/export/result.rb +21 -0
- data/lib/lithop/formats/stl.rb +60 -0
- data/lib/lithop/formats/three_mf/bambu_metadata.rb +105 -0
- data/lib/lithop/formats/three_mf/mesh.rb +55 -0
- data/lib/lithop/formats/three_mf/model_writer.rb +114 -0
- data/lib/lithop/formats/three_mf/part_writer.rb +49 -0
- data/lib/lithop/formats/three_mf.rb +128 -0
- data/lib/lithop/image/pipeline.rb +175 -0
- data/lib/lithop/image/placement.rb +54 -0
- data/lib/lithop/image/preview.rb +63 -0
- data/lib/lithop/image/svg_loader.rb +218 -0
- data/lib/lithop/instrumentation.rb +22 -0
- data/lib/lithop/job_directory.rb +55 -0
- data/lib/lithop/layer_structure.rb +134 -0
- data/lib/lithop/mesh/column_geometry.rb +161 -0
- data/lib/lithop/mesh/column_topology.rb +262 -0
- data/lib/lithop/mesh/geometry.rb +116 -0
- data/lib/lithop/mesh/validator.rb +123 -0
- data/lib/lithop/mesh/voxel_geometry.rb +113 -0
- data/lib/lithop/mesh/voxel_topology.rb +79 -0
- data/lib/lithop/model/filament_profile.rb +79 -0
- data/lib/lithop/model/material_display_map.rb +61 -0
- data/lib/lithop/model/material_model.rb +106 -0
- data/lib/lithop/model/palette.rb +101 -0
- data/lib/lithop/model/thickness.rb +45 -0
- data/lib/lithop/reference_three_mf.rb +119 -0
- data/lib/lithop/sampling_grid.rb +41 -0
- data/lib/lithop/settings.rb +295 -0
- data/lib/lithop/settings_file.rb +56 -0
- data/lib/lithop/version.rb +5 -0
- data/lib/lithop.rb +55 -0
- data/sig/lithop.rbs +291 -0
- metadata +155 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lithop
|
|
4
|
+
# Validates the user-supplied generation settings.
|
|
5
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
6
|
+
module SettingsValidation
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def validate!
|
|
10
|
+
validate_positive_values!
|
|
11
|
+
validate_thickness_range!
|
|
12
|
+
validate_output_limits!
|
|
13
|
+
validate_color_model!
|
|
14
|
+
validate_layout_profile!
|
|
15
|
+
validate_grid!
|
|
16
|
+
validate_layer_structure!
|
|
17
|
+
validate_advanced_layer_structure!
|
|
18
|
+
validate_margins!
|
|
19
|
+
validate_image_placement!
|
|
20
|
+
validate_nozzle_diameter!
|
|
21
|
+
validate_mesh_limit!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate_positive_values!
|
|
25
|
+
%i[width height minimum_thickness maximum_thickness pixel_size first_layer_height layer_height].each do |key|
|
|
26
|
+
value = public_send(key)
|
|
27
|
+
next if value.is_a?(Numeric) && value.finite? && value.positive?
|
|
28
|
+
|
|
29
|
+
raise ConfigurationError, "#{key} must be a positive finite number"
|
|
30
|
+
end
|
|
31
|
+
return if color_pixel_size.nil?
|
|
32
|
+
unless color_pixel_size.is_a?(Numeric) && color_pixel_size.finite? && color_pixel_size.positive?
|
|
33
|
+
raise ConfigurationError, "color_pixel_size must be a positive finite number or null"
|
|
34
|
+
end
|
|
35
|
+
return if color_pixel_size >= pixel_size
|
|
36
|
+
|
|
37
|
+
raise ConfigurationError, "color_pixel_size must be greater than or equal to pixel_size"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate_thickness_range!
|
|
41
|
+
return unless minimum_thickness > maximum_thickness
|
|
42
|
+
|
|
43
|
+
raise ConfigurationError, "minimum_thickness must not exceed maximum_thickness"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate_output_limits!
|
|
47
|
+
%i[maximum_input_pixels maximum_triangles].each do |key|
|
|
48
|
+
value = public_send(key)
|
|
49
|
+
next if value.is_a?(Integer) && value.positive?
|
|
50
|
+
|
|
51
|
+
raise ConfigurationError, "#{key} must be a positive integer"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_color_model!
|
|
56
|
+
return if Settings::COLOR_MODELS.include?(color_model)
|
|
57
|
+
|
|
58
|
+
raise ConfigurationError, "color_model must be one of: cmy"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_layout_profile!
|
|
62
|
+
return if Settings::LAYOUT_PROFILES.include?(layout_profile)
|
|
63
|
+
|
|
64
|
+
raise ConfigurationError, "layout_profile must be one of: #{Settings::LAYOUT_PROFILES.join(", ")}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def validate_grid!
|
|
68
|
+
raise ConfigurationError, "grid_columns and grid_rows must be set together" if grid_columns.nil? ^ grid_rows.nil?
|
|
69
|
+
return if grid_columns.nil?
|
|
70
|
+
return if grid_columns.is_a?(Integer) && grid_columns.positive? &&
|
|
71
|
+
grid_rows.is_a?(Integer) && grid_rows.positive?
|
|
72
|
+
|
|
73
|
+
raise ConfigurationError, "grid_columns and grid_rows must be positive integers or null"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def validate_layer_structure!
|
|
77
|
+
return if Settings::COLOR_LAYER_COUNTS.include?(color_layer_count)
|
|
78
|
+
|
|
79
|
+
raise ConfigurationError, "color_layer_count must be one of: #{Settings::COLOR_LAYER_COUNTS.join(", ")}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
83
|
+
def validate_advanced_layer_structure!
|
|
84
|
+
unless base_white_layers.is_a?(Integer) && base_white_layers.positive?
|
|
85
|
+
raise ConfigurationError, "base_white_layers must be a positive integer"
|
|
86
|
+
end
|
|
87
|
+
unless minimum_top_white_layers.is_a?(Integer) && minimum_top_white_layers >= 0
|
|
88
|
+
raise ConfigurationError, "minimum_top_white_layers must be a non-negative integer"
|
|
89
|
+
end
|
|
90
|
+
unless top_layer_height.nil? ||
|
|
91
|
+
(top_layer_height.is_a?(Numeric) && top_layer_height.finite? && top_layer_height.positive?)
|
|
92
|
+
raise ConfigurationError, "top_layer_height must be a positive finite number or null"
|
|
93
|
+
end
|
|
94
|
+
if !top_white_start.nil? &&
|
|
95
|
+
(!top_white_start.is_a?(Numeric) || !top_white_start.finite? || !top_white_start.positive?)
|
|
96
|
+
raise ConfigurationError, "top_white_start must be a positive finite number or null"
|
|
97
|
+
end
|
|
98
|
+
return if [true, false].include?(exact_maximum_thickness) && [true, false].include?(bambu_project)
|
|
99
|
+
|
|
100
|
+
raise ConfigurationError, "exact_maximum_thickness and bambu_project must be booleans"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_margins!
|
|
104
|
+
%i[margin_top margin_right margin_bottom margin_left].each do |key|
|
|
105
|
+
value = public_send(key)
|
|
106
|
+
next if value.is_a?(Numeric) && value.finite? && value >= 0.0
|
|
107
|
+
|
|
108
|
+
raise ConfigurationError, "#{key} must be a non-negative finite number"
|
|
109
|
+
end
|
|
110
|
+
return if margin_left + margin_right < width && margin_top + margin_bottom < height
|
|
111
|
+
|
|
112
|
+
raise ConfigurationError, "horizontal and vertical margins must each leave image area"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def validate_image_placement!
|
|
116
|
+
unless Settings::IMAGE_FITS.include?(image_fit)
|
|
117
|
+
raise ConfigurationError, "image_fit must be one of: #{Settings::IMAGE_FITS.join(", ")}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
%i[crop_position_x crop_position_y].each do |key|
|
|
121
|
+
value = public_send(key)
|
|
122
|
+
next if value.is_a?(Numeric) && value.finite? && value.between?(0.0, 1.0)
|
|
123
|
+
|
|
124
|
+
raise ConfigurationError, "#{key} must be a finite number from 0 to 1"
|
|
125
|
+
end
|
|
126
|
+
return if crop_scale.is_a?(Numeric) && crop_scale.finite? && crop_scale.positive? && crop_scale <= 1.0
|
|
127
|
+
|
|
128
|
+
raise ConfigurationError, "crop_scale must be a finite number greater than 0 and at most 1"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def validate_nozzle_diameter!
|
|
132
|
+
return if Settings::NOZZLE_DIAMETERS.include?(nozzle_diameter)
|
|
133
|
+
|
|
134
|
+
raise ConfigurationError, "nozzle_diameter must be one of: 0.2, 0.4"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def validate_mesh_limit!
|
|
138
|
+
return unless estimated_triangles > maximum_triangles
|
|
139
|
+
|
|
140
|
+
raise ConfigurationError, triangle_limit_message("estimated STL mesh", estimated_triangles)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def normalize_pigment_order(value)
|
|
144
|
+
entries = value.is_a?(String) ? value.split(",") : value
|
|
145
|
+
raise ConfigurationError, "pigment_order must be a comma-separated string or an array" unless entries.is_a?(Array)
|
|
146
|
+
|
|
147
|
+
order = entries.map { |entry| entry.to_s.strip.downcase.to_sym }
|
|
148
|
+
return order.freeze if order.length == Settings::PIGMENTS.length && order.sort == Settings::PIGMENTS.sort
|
|
149
|
+
|
|
150
|
+
raise ConfigurationError, "pigment_order must contain cyan, magenta, and yellow exactly once"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def validate_physical_minimum!
|
|
154
|
+
return if effective_minimum_thickness >= minimum_physical_thickness
|
|
155
|
+
|
|
156
|
+
raise ConfigurationError,
|
|
157
|
+
"physical CMYW output requires at least #{minimum_physical_thickness} mm thickness; " \
|
|
158
|
+
"set --min-thickness accordingly"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def minimum_physical_thickness
|
|
162
|
+
layer_structure.minimum_physical_thickness
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Validated dimensions, print parameters, and output limits for a generation job.
|
|
167
|
+
# rubocop:disable-next Metrics/ClassLength
|
|
168
|
+
class Settings
|
|
169
|
+
include SettingsValidation
|
|
170
|
+
|
|
171
|
+
MICRONS_PER_MM = LayerStructure::MICRONS_PER_MM
|
|
172
|
+
COLOR_MODELS = %w[cmy].freeze
|
|
173
|
+
NOZZLE_DIAMETERS = [0.2, 0.4].freeze
|
|
174
|
+
COLOR_LAYER_COUNTS = [4, 5, 7].freeze
|
|
175
|
+
LAYOUT_PROFILES = ["standard"].freeze
|
|
176
|
+
IMAGE_FITS = %w[cover contain].freeze
|
|
177
|
+
PIGMENTS = %i[cyan magenta yellow].freeze
|
|
178
|
+
MINIMUM_TOP_WHITE_LAYERS = LayerStructure::MINIMUM_TOP_WHITE_LAYERS
|
|
179
|
+
DEFAULTS = {
|
|
180
|
+
layout_profile: "standard",
|
|
181
|
+
width: 140.0,
|
|
182
|
+
height: 104.0,
|
|
183
|
+
margin_top: 0.0,
|
|
184
|
+
margin_right: 0.0,
|
|
185
|
+
margin_bottom: 0.0,
|
|
186
|
+
margin_left: 0.0,
|
|
187
|
+
image_fit: "cover",
|
|
188
|
+
crop_position_x: 0.5,
|
|
189
|
+
crop_position_y: 0.5,
|
|
190
|
+
crop_scale: 1.0,
|
|
191
|
+
minimum_thickness: 1.0,
|
|
192
|
+
maximum_thickness: 2.14689422,
|
|
193
|
+
pixel_size: 0.22,
|
|
194
|
+
color_pixel_size: nil,
|
|
195
|
+
grid_columns: 637,
|
|
196
|
+
grid_rows: 473,
|
|
197
|
+
first_layer_height: 0.1,
|
|
198
|
+
layer_height: 0.1,
|
|
199
|
+
base_white_layers: 2,
|
|
200
|
+
top_layer_height: 0.04,
|
|
201
|
+
top_white_start: 1.0,
|
|
202
|
+
minimum_top_white_layers: 0,
|
|
203
|
+
exact_maximum_thickness: true,
|
|
204
|
+
nozzle_diameter: 0.2,
|
|
205
|
+
color_model: "cmy",
|
|
206
|
+
color_layer_count: 7,
|
|
207
|
+
pigment_order: PIGMENTS,
|
|
208
|
+
bambu_project: true,
|
|
209
|
+
maximum_input_pixels: 20_000_000,
|
|
210
|
+
maximum_triangles: 5_000_000
|
|
211
|
+
}.freeze
|
|
212
|
+
attr_reader(
|
|
213
|
+
*DEFAULTS.keys, :effective_minimum_thickness, :effective_maximum_thickness, :layer_structure
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def initialize(**values)
|
|
217
|
+
profile = values.fetch(:layout_profile, DEFAULTS.fetch(:layout_profile)).to_s
|
|
218
|
+
values = without_default_grid(values)
|
|
219
|
+
settings = DEFAULTS.merge(values).merge(layout_profile: profile)
|
|
220
|
+
DEFAULTS.each_key { |key| instance_variable_set("@#{key}", settings.fetch(key)) }
|
|
221
|
+
@pigment_order = normalize_pigment_order(@pigment_order)
|
|
222
|
+
validate!
|
|
223
|
+
@layer_structure = LayerStructure.new(
|
|
224
|
+
first_layer_height:, layer_height:, color_layer_count:, minimum_thickness:, maximum_thickness:,
|
|
225
|
+
base_white_layers:, top_layer_height:, top_white_start:, minimum_top_white_layers:,
|
|
226
|
+
exact_maximum_thickness:
|
|
227
|
+
)
|
|
228
|
+
@effective_minimum_thickness = layer_structure.effective_minimum_thickness
|
|
229
|
+
@effective_maximum_thickness = layer_structure.effective_maximum_thickness
|
|
230
|
+
if effective_minimum_thickness > effective_maximum_thickness
|
|
231
|
+
raise ConfigurationError, "the requested thickness range contains no printable layer boundary"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
validate_physical_minimum!
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def without_default_grid(values)
|
|
238
|
+
dimensions = %i[width height pixel_size]
|
|
239
|
+
explicit_grid = values.key?(:grid_columns) || values.key?(:grid_rows)
|
|
240
|
+
return values if explicit_grid || dimensions.none? { |key| values.key?(key) }
|
|
241
|
+
|
|
242
|
+
values.merge(grid_columns: nil, grid_rows: nil)
|
|
243
|
+
end
|
|
244
|
+
private :without_default_grid
|
|
245
|
+
|
|
246
|
+
def columns = sampling_grid.columns
|
|
247
|
+
def rows = sampling_grid.rows
|
|
248
|
+
def effective_color_pixel_size = color_pixel_size || pixel_size
|
|
249
|
+
def fitted_grid? = !grid_columns.nil?
|
|
250
|
+
def pixel_size_x = width.fdiv(columns)
|
|
251
|
+
def pixel_size_y = height.fdiv(rows)
|
|
252
|
+
|
|
253
|
+
def estimated_triangles
|
|
254
|
+
(4 * columns * rows) + (4 * (columns + rows))
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def triangle_limit_message(label, triangle_count)
|
|
258
|
+
scale = Math.sqrt(triangle_count.fdiv(maximum_triangles))
|
|
259
|
+
suggested_pixel_size = ((pixel_size * scale * 100).ceil / 100.0).round(2)
|
|
260
|
+
"#{label} has #{triangle_count} triangles; maximum is #{maximum_triangles}. " \
|
|
261
|
+
"Increase pixel_size to about #{suggested_pixel_size} mm or reduce width/height"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def x_boundaries = sampling_grid.x_boundaries
|
|
265
|
+
def y_boundaries = sampling_grid.y_boundaries
|
|
266
|
+
def quantize_thickness(value) = layer_structure.quantize_thickness(value)
|
|
267
|
+
def margin_samples = sampling_grid.margin_samples
|
|
268
|
+
|
|
269
|
+
def sampling_grid
|
|
270
|
+
@sampling_grid ||= SamplingGrid.new(
|
|
271
|
+
width:, height:, pixel_size:,
|
|
272
|
+
margins: { top: margin_top, right: margin_right, bottom: margin_bottom, left: margin_left },
|
|
273
|
+
columns: grid_columns, rows: grid_rows
|
|
274
|
+
)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def color_sampling_grid
|
|
278
|
+
return sampling_grid if color_pixel_size.nil?
|
|
279
|
+
|
|
280
|
+
@color_sampling_grid ||= SamplingGrid.new(
|
|
281
|
+
width:, height:, pixel_size: effective_color_pixel_size,
|
|
282
|
+
margins: { top: margin_top, right: margin_right, bottom: margin_bottom, left: margin_left }
|
|
283
|
+
)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def to_h
|
|
287
|
+
DEFAULTS.keys.to_h { |key| [key, public_send(key)] }.merge(
|
|
288
|
+
effective_color_pixel_size: effective_color_pixel_size,
|
|
289
|
+
effective_minimum_thickness: effective_minimum_thickness,
|
|
290
|
+
effective_maximum_thickness: effective_maximum_thickness,
|
|
291
|
+
columns: columns, rows: rows, pixel_size_x: pixel_size_x, pixel_size_y: pixel_size_y
|
|
292
|
+
)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Lithop
|
|
6
|
+
# Loads validated generation overrides from a JSON settings file.
|
|
7
|
+
module SettingsFile
|
|
8
|
+
DEFAULT_PATH = File.join("config", "settings.json")
|
|
9
|
+
SCHEMA_VERSION = 1
|
|
10
|
+
ALLOWED_KEYS = (Settings::DEFAULTS.keys.map(&:to_s) + ["schema_version"]).freeze
|
|
11
|
+
Result = Data.define(:path, :values)
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def load(path, required: false)
|
|
16
|
+
expanded = File.expand_path(path)
|
|
17
|
+
unless File.exist?(expanded)
|
|
18
|
+
raise ConfigurationError, "settings file does not exist: #{expanded}" if required
|
|
19
|
+
|
|
20
|
+
return Result.new(path: nil, values: {})
|
|
21
|
+
end
|
|
22
|
+
raise ConfigurationError, "settings path is not a file: #{expanded}" unless File.file?(expanded)
|
|
23
|
+
|
|
24
|
+
data = JSON.parse(File.read(expanded))
|
|
25
|
+
raise ConfigurationError, "settings file must contain a JSON object: #{expanded}" unless data.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
validate_keys!(data, expanded)
|
|
28
|
+
validate_schema!(data, expanded)
|
|
29
|
+
values = data.except("schema_version").transform_keys(&:to_sym)
|
|
30
|
+
values[:color_model] = values[:color_model].downcase if values[:color_model].is_a?(String)
|
|
31
|
+
values[:image_fit] = values[:image_fit].downcase if values[:image_fit].is_a?(String)
|
|
32
|
+
values[:layout_profile] = values[:layout_profile].downcase if values[:layout_profile].is_a?(String)
|
|
33
|
+
Result.new(path: expanded, values:)
|
|
34
|
+
rescue JSON::ParserError => e
|
|
35
|
+
raise ConfigurationError, "settings file is not valid JSON: #{expanded}: #{e.message}"
|
|
36
|
+
rescue SystemCallError => e
|
|
37
|
+
raise ConfigurationError, "could not read settings file: #{expanded}: #{e.message}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate_keys!(data, path)
|
|
41
|
+
unknown = data.keys - ALLOWED_KEYS
|
|
42
|
+
return if unknown.empty?
|
|
43
|
+
|
|
44
|
+
raise ConfigurationError, "settings file contains unknown keys: #{unknown.sort.join(", ")}: #{path}"
|
|
45
|
+
end
|
|
46
|
+
private_class_method :validate_keys!
|
|
47
|
+
|
|
48
|
+
def validate_schema!(data, path)
|
|
49
|
+
version = data.fetch("schema_version", SCHEMA_VERSION)
|
|
50
|
+
return if version.eql?(SCHEMA_VERSION)
|
|
51
|
+
|
|
52
|
+
raise ConfigurationError, "unsupported settings schema_version #{version.inspect}: #{path}"
|
|
53
|
+
end
|
|
54
|
+
private_class_method :validate_schema!
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/lithop.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lithop/version"
|
|
4
|
+
|
|
5
|
+
module Lithop
|
|
6
|
+
class Error < StandardError; end
|
|
7
|
+
class ConfigurationError < Error; end
|
|
8
|
+
class InputError < Error; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require_relative "lithop/sampling_grid"
|
|
12
|
+
require_relative "lithop/layer_structure"
|
|
13
|
+
require_relative "lithop/settings"
|
|
14
|
+
require_relative "lithop/settings_file"
|
|
15
|
+
require_relative "lithop/job_directory"
|
|
16
|
+
require_relative "lithop/instrumentation"
|
|
17
|
+
require_relative "lithop/model/thickness"
|
|
18
|
+
require_relative "lithop/model/palette"
|
|
19
|
+
require_relative "lithop/model/material_display_map"
|
|
20
|
+
require_relative "lithop/model/filament_profile"
|
|
21
|
+
require_relative "lithop/model/material_model"
|
|
22
|
+
require_relative "lithop/image/placement"
|
|
23
|
+
require_relative "lithop/image/svg_loader"
|
|
24
|
+
require_relative "lithop/image/pipeline"
|
|
25
|
+
require_relative "lithop/image/preview"
|
|
26
|
+
require_relative "lithop/mesh/geometry"
|
|
27
|
+
require_relative "lithop/mesh/voxel_topology"
|
|
28
|
+
require_relative "lithop/mesh/voxel_geometry"
|
|
29
|
+
require_relative "lithop/mesh/column_topology"
|
|
30
|
+
require_relative "lithop/mesh/column_geometry"
|
|
31
|
+
require_relative "lithop/mesh/validator"
|
|
32
|
+
require_relative "lithop/formats/stl"
|
|
33
|
+
require_relative "lithop/formats/three_mf"
|
|
34
|
+
require_relative "lithop/reference_three_mf"
|
|
35
|
+
require_relative "lithop/calibration/target"
|
|
36
|
+
require_relative "lithop/calibration/target_svg"
|
|
37
|
+
require_relative "lithop/calibration/profile_measurements"
|
|
38
|
+
require_relative "lithop/calibration/profile"
|
|
39
|
+
require_relative "lithop/calibration/recipe_selector"
|
|
40
|
+
require_relative "lithop/calibration/thickness_selector"
|
|
41
|
+
require_relative "lithop/calibration/export"
|
|
42
|
+
require_relative "lithop/export/calibration"
|
|
43
|
+
require_relative "lithop/export/result"
|
|
44
|
+
require_relative "lithop/export/color_metadata"
|
|
45
|
+
require_relative "lithop/export/metadata"
|
|
46
|
+
require_relative "lithop/export/planning"
|
|
47
|
+
require_relative "lithop/export/print_readme"
|
|
48
|
+
require_relative "lithop/export/previews"
|
|
49
|
+
require_relative "lithop/export/generation"
|
|
50
|
+
require_relative "lithop/export/job"
|
|
51
|
+
require_relative "lithop/cli/image_options"
|
|
52
|
+
require_relative "lithop/cli/options"
|
|
53
|
+
require_relative "lithop/cli/settings"
|
|
54
|
+
require_relative "lithop/cli/calibration"
|
|
55
|
+
require_relative "lithop/cli"
|