kupcad 0.1.10-x86_64-linux-gnu
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/LICENSE +661 -0
- data/README.md +98 -0
- data/lib/kupcad/native/THIRD_PARTY_NOTICES.md +29 -0
- data/lib/kupcad/native/libkupcad.so +0 -0
- data/lib/kupcad/native/licenses/AllertaStencil-OFL.txt +93 -0
- data/lib/kupcad/native/licenses/NotoSymbols-OFL.txt +98 -0
- data/lib/kupcad/native/licenses/RobotoSerif-OFL.txt +93 -0
- data/lib/kupcad/native/licenses/draco/LICENSE +252 -0
- data/lib/kupcad/native/licenses/eigen/LICENSE +373 -0
- data/lib/kupcad/native/licenses/eigen/LICENSES/Apache-2.0.txt +203 -0
- data/lib/kupcad/native/licenses/eigen/LICENSES/BSD-3-Clause-Open-MPI.txt +34 -0
- data/lib/kupcad/native/licenses/eigen/LICENSES/BSD-3-Clause.txt +26 -0
- data/lib/kupcad/native/licenses/eigen/LICENSES/LicenseRef-MINPACK.txt +51 -0
- data/lib/kupcad/native/licenses/eigen/LICENSES/MPL-2.0.txt +373 -0
- data/lib/kupcad/native/licenses/harfbuzz/COPYING +42 -0
- data/lib/kupcad/native/licenses/harfbuzz/ms-use/COPYING +21 -0
- data/lib/kupcad/native/licenses/lsp-kit-MIT.txt +21 -0
- data/lib/kupcad/native/licenses/manifold/LICENSE +201 -0
- data/lib/kupcad/native/licenses/onetbb/LICENSE.txt +201 -0
- data/lib/kupcad/native/licenses/onetbb/third-party-programs.txt +198 -0
- data/lib/kupcad/native/licenses/sheenbidi/LICENSE +202 -0
- data/lib/kupcad/native/licenses/tatfi/LICENSE +373 -0
- data/lib/kupcad/native/licenses/zig-MIT.txt +21 -0
- data/lib/kupcad/native.rb +77 -0
- data/lib/kupcad/version.rb +6 -0
- data/lib/kupcad.rb +199 -0
- data/sig/kupcad.rbs +61 -0
- metadata +90 -0
data/lib/kupcad.rb
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative 'kupcad/version'
|
|
5
|
+
require_relative 'kupcad/native'
|
|
6
|
+
|
|
7
|
+
# KupCAD from Ruby: build the model of a KupCAD script, with its parameters, in any format the
|
|
8
|
+
# CLI exports, and format, lint and read the parameters of scripts. It runs libkupcad, the
|
|
9
|
+
# KupCAD core as a C library, in the process; a script reads only the files given with it.
|
|
10
|
+
#
|
|
11
|
+
# model = Kupcad.build(File.read('bracket.kup'), format: :stl, params: { width: 60 })
|
|
12
|
+
# File.binwrite('bracket.stl', model.data)
|
|
13
|
+
module Kupcad
|
|
14
|
+
# What every error of the gem's is: raised itself when libkupcad fails on its own (a bug of
|
|
15
|
+
# the core's)
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
|
|
18
|
+
# The script doesn't parse or compile, raises (a value one of its parameters refuses among
|
|
19
|
+
# it: "ValueError: ..."), runs past its time or memory limit, or leaves no model to export.
|
|
20
|
+
# The message says where and why: "2:5: Invalid expression ...", "RuntimeError: Plate too
|
|
21
|
+
# thin".
|
|
22
|
+
class ScriptError < Error
|
|
23
|
+
# What the script printed and warned of before it failed (a Report); nil when nothing ran
|
|
24
|
+
# (Kupcad.format's, check's and params')
|
|
25
|
+
attr_reader :report
|
|
26
|
+
|
|
27
|
+
def initialize(message, report = nil)
|
|
28
|
+
super(message)
|
|
29
|
+
@report = report
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Something the script warned of, at its line and column (1-based; nil where it wasn't
|
|
34
|
+
# running, as the model was meshed), in `file`, the imported file whose code warned (nil for
|
|
35
|
+
# the script's own)
|
|
36
|
+
RunWarning = Data.define(:message, :line, :column, :file)
|
|
37
|
+
|
|
38
|
+
# What a run printed (`puts`, `print`, `warn`), up to 64 KB (`output_cut` past them), and
|
|
39
|
+
# what it warned of
|
|
40
|
+
Report = Data.define(:output, :output_cut, :warnings) do
|
|
41
|
+
def self.from_json(json)
|
|
42
|
+
fields = JSON.parse(json, symbolize_names: true)
|
|
43
|
+
warnings = fields[:warnings].map do |warning|
|
|
44
|
+
RunWarning.new(message: warning[:message], line: warning[:line], column: warning[:column], file: warning[:file])
|
|
45
|
+
end
|
|
46
|
+
new(output: fields[:output], output_cut: fields[:outputCut], warnings:)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# A built model: its bytes in `format` (binary), and the run's report
|
|
51
|
+
Model = Data.define(:data, :format, :report)
|
|
52
|
+
|
|
53
|
+
# A lint finding or a syntax error, as `kupcad check --format json` gives it: its severity
|
|
54
|
+
# ("error", "warning" or "info"), its rule (nil for a syntax error), its code, its message,
|
|
55
|
+
# where it starts and ends (1-based), and its fix (a Hash, or nil)
|
|
56
|
+
Finding = Data.define(:severity, :rule, :code, :message, :line, :column, :end_line, :end_column, :fix)
|
|
57
|
+
|
|
58
|
+
# The most a limit, or a file's length, can be as libkupcad takes it
|
|
59
|
+
UINT64_MAX = (2**64) - 1
|
|
60
|
+
UINT32_MAX = (2**32) - 1
|
|
61
|
+
|
|
62
|
+
# The encodings a String is passed in as it is: UTF-8, and those File.binread and File.read in
|
|
63
|
+
# a C locale tag whatever bytes they read with
|
|
64
|
+
RAW_ENCODINGS = [Encoding::UTF_8, Encoding::BINARY, Encoding::US_ASCII].freeze
|
|
65
|
+
private_constant :UINT64_MAX, :UINT32_MAX, :RAW_ENCODINGS
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
# The version of the libkupcad loaded: the gem's own, unless KUPCAD_LIBRARY names another
|
|
69
|
+
def library_version
|
|
70
|
+
library_info[:version]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# `source` formatted, as `kupcad fmt` formats it. Raises ScriptError when it doesn't parse.
|
|
74
|
+
def format(source)
|
|
75
|
+
text(:kupcad_format, source)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The findings of `source`: lint findings and syntax errors, an Array of Finding
|
|
79
|
+
def check(source)
|
|
80
|
+
JSON.parse(text(:kupcad_check, source), symbolize_names: true).map do |finding|
|
|
81
|
+
Finding.new(severity: finding[:severity], rule: finding[:rule], code: finding[:code],
|
|
82
|
+
message: finding[:message], line: finding[:line], column: finding[:column],
|
|
83
|
+
end_line: finding[:endLine], end_column: finding[:endColumn], fix: finding[:fix])
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The parameters `source` declares and its metadata, the schema `kupcad doc` prints:
|
|
88
|
+
# { meta: { title: ... }, parameters: [{ name: ..., type: ..., default: ... }] }
|
|
89
|
+
def params(source)
|
|
90
|
+
JSON.parse(text(:kupcad_params, source), symbolize_names: true)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Runs `source` and builds its model in `format` (one of FORMATS), a Model.
|
|
94
|
+
#
|
|
95
|
+
# params: the parameters' values by name, as `kupcad build -p` takes them
|
|
96
|
+
# ({ width: 60, label: 'A', holes: [[5, 5], [35, 5]] }).
|
|
97
|
+
# files: the files the script imports, by their path from its folder, text or binary
|
|
98
|
+
# ({ 'lib/gears.kup' => File.read('lib/gears.kup'), 'parts/base.stl' => File.binread(...) }).
|
|
99
|
+
# memory_limit: the most memory, in bytes, the script may hold; nil for no limit.
|
|
100
|
+
# timeout: the most time, in seconds, the build may take; nil (or Float::INFINITY) for no
|
|
101
|
+
# limit. The time is checked every few thousand instructions and between geometry
|
|
102
|
+
# operations.
|
|
103
|
+
# draco: compress a GLB's or a glTF's meshes with Draco.
|
|
104
|
+
#
|
|
105
|
+
# Raises ScriptError when the script fails, with what it printed and warned of until then,
|
|
106
|
+
# ArgumentError for a format that isn't one, a limit that isn't positive, or parameter
|
|
107
|
+
# values JSON can't hold, and TypeError for a source or a file's content that isn't a String.
|
|
108
|
+
def build(source, format: :stl, params: {}, files: {}, memory_limit: nil, timeout: nil, draco: false)
|
|
109
|
+
format = format.to_sym
|
|
110
|
+
unless FORMATS.include?(format)
|
|
111
|
+
raise ArgumentError, "#{format.inspect} isn't a format: one of #{FORMATS.join(', ')}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
data, report = call(:kupcad_build, *Native.buffer(utf8(source, 'source')), format.to_s,
|
|
115
|
+
*Native.buffer(params_json(params)), *Native.buffer(pack(files)),
|
|
116
|
+
limit(memory_limit, 'memory_limit'), milliseconds(timeout), draco ? true : false)
|
|
117
|
+
Model.new(data:, format:, report: Report.from_json(report))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# What the library says of itself: { version:, protocolVersion:, os:, arch:, formats: }
|
|
123
|
+
def library_info
|
|
124
|
+
JSON.parse(Native.utf8(call(:kupcad_version).first), symbolize_names: true)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The data of libkupcad's `function` for `source`, as UTF-8 text; raises what failed
|
|
128
|
+
def text(function, source)
|
|
129
|
+
Native.utf8(call(function, *Native.buffer(utf8(source, 'source'))).first)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# [data, report] of libkupcad's `function` called with `arguments`, the data binary; raises
|
|
133
|
+
# what failed. An interrupt that comes while it runs (Timeout, Ctrl-C, Thread#raise) is
|
|
134
|
+
# raised once the result is read and freed: raised as the call returned, it lost the result.
|
|
135
|
+
def call(function, *arguments)
|
|
136
|
+
status, data, message, report = Thread.handle_interrupt(Object => :never) do
|
|
137
|
+
Native.take(Native.public_send(function, *arguments))
|
|
138
|
+
end
|
|
139
|
+
case status
|
|
140
|
+
when :ok then [data, report]
|
|
141
|
+
when :script_error then raise ScriptError.new(message, report && Report.from_json(report))
|
|
142
|
+
when :invalid_argument then raise ArgumentError, message
|
|
143
|
+
when :out_of_memory then raise NoMemoryError, message
|
|
144
|
+
else raise Error, message
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# `text` as libkupcad reads it, UTF-8: transcoded from the encoding it says it is in
|
|
149
|
+
def utf8(text, name)
|
|
150
|
+
raise TypeError, "#{name} is #{text.class}: a String" unless text.is_a?(String)
|
|
151
|
+
|
|
152
|
+
RAW_ENCODINGS.include?(text.encoding) ? text : text.encode(Encoding::UTF_8)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def params_json(params)
|
|
156
|
+
params.empty? ? '' : JSON.generate(params)
|
|
157
|
+
rescue JSON::GeneratorError, JSON::NestingError => e
|
|
158
|
+
raise ArgumentError, "parameters: #{e.message}"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# A limit as the library takes it, 0 for none; one past what it holds is none either
|
|
162
|
+
def limit(value, name)
|
|
163
|
+
return 0 if value.nil?
|
|
164
|
+
unless value.is_a?(Integer) && value.positive?
|
|
165
|
+
raise ArgumentError, "#{name} is #{value.inspect}: a positive Integer, or nil for none"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
[value, UINT64_MAX].min
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Seconds as the library's whole milliseconds, at least 1; 0 for no limit
|
|
172
|
+
def milliseconds(seconds)
|
|
173
|
+
return 0 if seconds.nil?
|
|
174
|
+
unless seconds.is_a?(Numeric) && seconds.real? && seconds.positive?
|
|
175
|
+
raise ArgumentError, "timeout is #{seconds.inspect}: positive seconds, or nil for none"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
milliseconds = seconds * 1000
|
|
179
|
+
milliseconds >= UINT64_MAX ? UINT64_MAX : [milliseconds.round, 1].max
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# The files packed as kupcad.h describes: each its path's length, its path (UTF-8), its
|
|
183
|
+
# content's length and its content (its bytes), each length 4 bytes, little-endian
|
|
184
|
+
def pack(files)
|
|
185
|
+
files.each_with_object(String.new(encoding: Encoding::BINARY)) do |(path, content), packed|
|
|
186
|
+
raise TypeError, "files: #{path} is #{content.class}, not its content as a String" unless content.is_a?(String)
|
|
187
|
+
|
|
188
|
+
[utf8(path.to_s, 'a path'), content].each do |field|
|
|
189
|
+
raise ArgumentError, "files: #{path} is past 4 GiB, the most a file holds" if field.bytesize > UINT32_MAX
|
|
190
|
+
|
|
191
|
+
packed << [field.bytesize].pack('V') << field.b
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# The formats a model can be built in, as the library loaded names them
|
|
198
|
+
FORMATS = library_info[:formats].map(&:to_sym).freeze
|
|
199
|
+
end
|
data/sig/kupcad.rbs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Kupcad
|
|
2
|
+
VERSION: String
|
|
3
|
+
FORMATS: Array[Symbol]
|
|
4
|
+
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class ScriptError < Error
|
|
9
|
+
attr_reader report: Report?
|
|
10
|
+
|
|
11
|
+
def initialize: (String message, ?Report? report) -> void
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class RunWarning < Data
|
|
15
|
+
attr_reader message: String
|
|
16
|
+
attr_reader line: Integer?
|
|
17
|
+
attr_reader column: Integer?
|
|
18
|
+
attr_reader file: String?
|
|
19
|
+
|
|
20
|
+
def self.new: (message: String, line: Integer?, column: Integer?, file: String?) -> RunWarning
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Report < Data
|
|
24
|
+
attr_reader output: String
|
|
25
|
+
attr_reader output_cut: bool
|
|
26
|
+
attr_reader warnings: Array[RunWarning]
|
|
27
|
+
|
|
28
|
+
def self.new: (output: String, output_cut: bool, warnings: Array[RunWarning]) -> Report
|
|
29
|
+
def self.from_json: (String json) -> Report
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Model < Data
|
|
33
|
+
attr_reader data: String
|
|
34
|
+
attr_reader format: Symbol
|
|
35
|
+
attr_reader report: Report
|
|
36
|
+
|
|
37
|
+
def self.new: (data: String, format: Symbol, report: Report) -> Model
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Finding < Data
|
|
41
|
+
attr_reader severity: String
|
|
42
|
+
attr_reader rule: String?
|
|
43
|
+
attr_reader code: String?
|
|
44
|
+
attr_reader message: String
|
|
45
|
+
attr_reader line: Integer
|
|
46
|
+
attr_reader column: Integer
|
|
47
|
+
attr_reader end_line: Integer
|
|
48
|
+
attr_reader end_column: Integer
|
|
49
|
+
attr_reader fix: Hash[Symbol, untyped]?
|
|
50
|
+
|
|
51
|
+
def self.new: (severity: String, rule: String?, code: String?, message: String, line: Integer, column: Integer,
|
|
52
|
+
end_line: Integer, end_column: Integer, fix: Hash[Symbol, untyped]?) -> Finding
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.library_version: () -> String
|
|
56
|
+
def self.format: (String source) -> String
|
|
57
|
+
def self.check: (String source) -> Array[Finding]
|
|
58
|
+
def self.params: (String source) -> Hash[Symbol, untyped]
|
|
59
|
+
def self.build: (String source, ?format: Symbol | String, ?params: Hash[Symbol | String, untyped],
|
|
60
|
+
?files: Hash[_ToS, String], ?memory_limit: Integer?, ?timeout: Numeric?, ?draco: boolish) -> Model
|
|
61
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kupcad
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.10
|
|
5
|
+
platform: x86_64-linux-gnu
|
|
6
|
+
authors:
|
|
7
|
+
- Oleksii Vasyliev
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.15'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.15'
|
|
26
|
+
description: Builds the model of a KupCAD script, with its parameters, as STL, 3MF,
|
|
27
|
+
glTF, OBJ, STEP, SVG or DXF, and formats, lints and reads the parameters of scripts,
|
|
28
|
+
in process, through libkupcad, the KupCAD core as a C library, which each platform
|
|
29
|
+
gem carries.
|
|
30
|
+
email:
|
|
31
|
+
- le0pard@users.noreply.github.com
|
|
32
|
+
executables: []
|
|
33
|
+
extensions: []
|
|
34
|
+
extra_rdoc_files: []
|
|
35
|
+
files:
|
|
36
|
+
- LICENSE
|
|
37
|
+
- README.md
|
|
38
|
+
- lib/kupcad.rb
|
|
39
|
+
- lib/kupcad/native.rb
|
|
40
|
+
- lib/kupcad/native/THIRD_PARTY_NOTICES.md
|
|
41
|
+
- lib/kupcad/native/libkupcad.so
|
|
42
|
+
- lib/kupcad/native/licenses/AllertaStencil-OFL.txt
|
|
43
|
+
- lib/kupcad/native/licenses/NotoSymbols-OFL.txt
|
|
44
|
+
- lib/kupcad/native/licenses/RobotoSerif-OFL.txt
|
|
45
|
+
- lib/kupcad/native/licenses/draco/LICENSE
|
|
46
|
+
- lib/kupcad/native/licenses/eigen/LICENSE
|
|
47
|
+
- lib/kupcad/native/licenses/eigen/LICENSES/Apache-2.0.txt
|
|
48
|
+
- lib/kupcad/native/licenses/eigen/LICENSES/BSD-3-Clause-Open-MPI.txt
|
|
49
|
+
- lib/kupcad/native/licenses/eigen/LICENSES/BSD-3-Clause.txt
|
|
50
|
+
- lib/kupcad/native/licenses/eigen/LICENSES/LicenseRef-MINPACK.txt
|
|
51
|
+
- lib/kupcad/native/licenses/eigen/LICENSES/MPL-2.0.txt
|
|
52
|
+
- lib/kupcad/native/licenses/harfbuzz/COPYING
|
|
53
|
+
- lib/kupcad/native/licenses/harfbuzz/ms-use/COPYING
|
|
54
|
+
- lib/kupcad/native/licenses/lsp-kit-MIT.txt
|
|
55
|
+
- lib/kupcad/native/licenses/manifold/LICENSE
|
|
56
|
+
- lib/kupcad/native/licenses/onetbb/LICENSE.txt
|
|
57
|
+
- lib/kupcad/native/licenses/onetbb/third-party-programs.txt
|
|
58
|
+
- lib/kupcad/native/licenses/sheenbidi/LICENSE
|
|
59
|
+
- lib/kupcad/native/licenses/tatfi/LICENSE
|
|
60
|
+
- lib/kupcad/native/licenses/zig-MIT.txt
|
|
61
|
+
- lib/kupcad/version.rb
|
|
62
|
+
- sig/kupcad.rbs
|
|
63
|
+
homepage: https://kupcad.org
|
|
64
|
+
licenses:
|
|
65
|
+
- AGPL-3.0-only
|
|
66
|
+
metadata:
|
|
67
|
+
homepage_uri: https://kupcad.org
|
|
68
|
+
source_code_uri: https://github.com/kupcad/kupcad/tree/main/bindings/ruby
|
|
69
|
+
documentation_uri: https://docs.kupcad.org
|
|
70
|
+
changelog_uri: https://github.com/kupcad/kupcad/releases
|
|
71
|
+
bug_tracker_uri: https://github.com/kupcad/kupcad/issues
|
|
72
|
+
rubygems_mfa_required: 'true'
|
|
73
|
+
rdoc_options: []
|
|
74
|
+
require_paths:
|
|
75
|
+
- lib
|
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '3.2'
|
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
requirements: []
|
|
87
|
+
rubygems_version: 4.0.20
|
|
88
|
+
specification_version: 4
|
|
89
|
+
summary: 'KupCAD for Ruby: build parametric CAD models from KupCAD scripts'
|
|
90
|
+
test_files: []
|