lens_protocol 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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +98 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +106 -0
  10. data/Guardfile +42 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +69 -0
  13. data/Rakefile +6 -0
  14. data/bin/console +11 -0
  15. data/bin/setup +8 -0
  16. data/examples/images/R360_1.png +0 -0
  17. data/examples/oma/R1000_1.oma +213 -0
  18. data/examples/oma/R1000_2.oma +213 -0
  19. data/examples/oma/R1000_3.oma +217 -0
  20. data/examples/oma/R360_1.oma +78 -0
  21. data/examples/oma/R360_2.oma +75 -0
  22. data/examples/oma/R360_3.oma +276 -0
  23. data/examples/oma/TRCFMT6.oma +28 -0
  24. data/examples/public/styles.css +22 -0
  25. data/examples/svg.rb +10 -0
  26. data/examples/views/index.erb +26 -0
  27. data/lens_protocol.gemspec +33 -0
  28. data/lib/lens_protocol.rb +18 -0
  29. data/lib/lens_protocol/errors.rb +10 -0
  30. data/lib/lens_protocol/oma.rb +14 -0
  31. data/lib/lens_protocol/oma/formatter.rb +22 -0
  32. data/lib/lens_protocol/oma/message.rb +138 -0
  33. data/lib/lens_protocol/oma/parser.rb +24 -0
  34. data/lib/lens_protocol/oma/record.rb +19 -0
  35. data/lib/lens_protocol/oma/type/base.rb +97 -0
  36. data/lib/lens_protocol/oma/type/integer.rb +15 -0
  37. data/lib/lens_protocol/oma/type/numeric.rb +24 -0
  38. data/lib/lens_protocol/oma/type/r.rb +17 -0
  39. data/lib/lens_protocol/oma/type/text.rb +8 -0
  40. data/lib/lens_protocol/oma/type/trcfmt.rb +30 -0
  41. data/lib/lens_protocol/oma/types.rb +84 -0
  42. data/lib/lens_protocol/svg.rb +45 -0
  43. data/lib/lens_protocol/version.rb +3 -0
  44. metadata +213 -0
@@ -0,0 +1,28 @@
1
+ REQ=FIL
2
+ DO=B
3
+ BEVM=0.00;0.00
4
+ BEVP=4;4
5
+ CIRC=151.860;151.860
6
+ DBL=17.00
7
+ ETYP=3
8
+ FCSGIN=1.50;1.50
9
+ FCSGUP=4.00;4.00
10
+ FPINB=0.00;0.00
11
+ FTYP=3
12
+ GDEPTH=0.50;0.50
13
+ GWIDTH=0.60;0.60
14
+ HBOX=53.99;53.99
15
+ NPD=34.00;34.00
16
+ IPD=34.00;34.00
17
+ SEGHT=22.00;22.00
18
+ OCHT=22.00;22.00
19
+ PINB=0.20;0.20
20
+ POLISH=1
21
+ TRCFMT=6;360;E;R;F
22
+ R=6167514141436D71764D2B7958762F524C337855574A4939485372724B525553742B3733737151776F5A554871777046704B39455452706378337A4744544C4F48397961556D33657938627A63394D6B4E415A766E7A46626F4B462B586962644874692F435071432F445670717044755936734274304E797536472F356D4E44305474302F56584D4D75614441743566346662484B474C6A4C314E67433765703562424B7474584D495945753730534A6A45776A4575384A783930624F326D782B67715A6E6F466A4E6D51724C325A3334467945614646615A2B423264793066505A4D6B64585968584E3259504E6744713235574D634B353233494A304D746B5865347A346E776D6E6B6136635649576357475A32454133634A68624D2F63616C494F38724B4C7556584F7176634A4B567768336A387762767430422F636633766537502F31784173694F366173424134695A727970324249757657495051756C5364684F4B327043387A75344C534367544F54424A6F42384B41356449674A506B523678697146796B52464230686E4142594C684E2B73385059636D5A4E675362494F454542514D745A664A75395145743957674C4149754E32737A42494872384B302F376C375965792F364C7131597661416C4D4E3954345853595138685964624F617571674E7843554C4634736458635469485272354D2B5533712F6D477A6E656F6B5673654B7245517A675754625248474A667A3341593242576C6E733169466D6A53516F485335586863597854676274435955666B376A36466E32576136464D6572736E44516874374379656D44615441782F314A47377A526E716777504C506F4D6F6D422F54766D63324B4757306933746C4750386E4A614A684D6549636E504145433761534A347A35516353666A50442F4448385855764431424A4456304D387A6E4871636C32614B346B4B7754444D39317264514B3148526A6A695A69773942774E69484878583246475641582F4F2B752F666467375574784E7A6B7159573841437177327741304D3976322B3032694B66755A7A534856576E4E656941382F384148714F76494B646136692B53634F6B4F6A6A7478777975697767724752386833613677314C444D666255443157707544354C795867554D3651637274463963784D497547674374387563334B48786F57567738472F484A3961625731506B624749473173524D4C346255556D4F684678766F75434E637879314C314F4E703331445530655979726F654C357849444B5937426C51415872464A564A336F6B4A32632B4A76587A2B4576552F67446944457749665177735A34596556306253422F567151546B6A786A584C6D6574336D714B4775477A61697063326F75332B617637567A3753424C567835396F667878646741666F5854657A473949306F5A31485A7057314B35556331762F435743705A38784C395569316E34575936393757612F336C69416B7447386D6D55336E5465486F6134344379696A617073537078716A344D344678454F755763375336635A4E6454502B476A543570415935633037396A4D69493636713931445033472B5669347A5833306B797565342B5A486D646B4A69366349694275506A49786755723849494438746B5258303751644F2F496F6F556A716B6E735455416E472F526D6E41764332434C515257704D7A4D6930454D6E5866574B636E6D6F487552504A63374D5235535744396D444D54393274786655753949436E3045435761755933386E374663553436526F51376564687235346431615166507044786A6552756F6468303144745267647A4A7661506630596D534270636E486137723769317036526D6D7637574A61624747516965796A6C32424D494A785373632F3762746D302F745A6D6237723974675A2B6D6F515A7A6B4C53536A45576F385A356A324D4D58446E586C2B316B5A7462497174653875726F7A7773724A43386F7A6D595259443273346750346F4A5A374B3970364A636D75644D665633496762554B706F6E716C56505469497076762F71494C375231356349582B322F3538494732736D35427236657044572F4C7759616D51514867794D55636B
23
+ TRCFMT=6;360;E;L;F
24
+ R=61675141414D4E6C4D58334355662F61433866447949556F7A747534612B36753438664A634F5967394F7137657A625A5052714652573954624B5572744852493458362B756951372F544A6441424673366C2F464B3143714E62664B30396D583939512B54346C644E3967563173657948302F7938614D62586B67756553396E3171474D6C54766E48666844334E3070365A4E414C6441694E4D685A6E43373851594E4B495533794C37553331434B4E44585251426C46763063484B6248437476664B58794E6A75584362634665396A596C30786A3366596C4C5466414D6F5A706F73536F484D44563072455A7663352F7334594954494B5A432B635237684941427874526633746430757571573058762B48615432736C2F4366397642343174574959536B575A4C366E2B634E6A706468722B46357468573068637A6F396845705472516C43304D717077776C683535646F4C514E31625041704D3175724D4F6937483052594178517734674645754E5441666B307056695A4D4E2B73467A584A5434436A654E7961676D6C4E3843694E6C674A35316A544E33566E5756554E6645634C4F7059752F73314E53445A7869733973626970642F593277635751467975627175394D5954764E666977653655704D514A6D5038537879372F726973593570435969303245722F424D7A525A564B6262437356314D646134446C4A38316F4D45614C617044656A2B4F4F6C7550774D70704E5245696D5862587346505159335549645872564864624375302B30333353654F574443336E6C6E336F6B4E506A45485739686D48564F493454456B6B65786F4C6C506C69394F454C2B476D53536832757A526B62795550525975697A5673417559763638655A775548556245555A6C4753594436646770733659766B726C524532793278644C3451556F55534368305678442B577149686D31705843764D4E4656616848643157336641514F57344E6D335452624B47594A786C6F326A4A46344557565A5A452F425844655230396555414968424567736B474E794B657731765A78384F7949646443486A726670457367337A4F6F734A677755685765436E466A51763571413069696F56507468726F67466568583668457351754635507A526E677047574A746A5059652F446E79764476386530685869346A744E4F4D377657394B744B696D6E76324E744C71387358794F6535494B723261773845746E766D78626867367A43685342767A74585A5238337838796A7367344B7675304B535663547763656A5777735A4277457A482B58484966416F7366584D5A363065705053697746304F3359317349394C75693149534F396B58537577684A41435A6D767A71655167304D6456414C36476433564B6A5853355658547A7071793054307A69452F5235483636326C763151636B436F36724B44502B4D505163446567486B5744502B4E702B426175677A6F31564742353271634441777741494168566E71307356694153474E732B2F66644D4237544239576F797A4444584F55665A4C717068796F6E536556467448376D70482F4E786669485439483878465950346265665061332F624661466D2B797359616B546450555255785A67487869443872784F385678314C6B56303035737045776E39537278334A513077765977715150387A5555793234426C3355567A356977497331794A494762502B73574148417255705254566575697A5149536A347538314C524E6974576F5356363742466C426E7473324D624465336869684D36394939652F53596C4255696A6D4F3949706959336D377A6137425933716D504E694561395A71676C463434776E48574F74336D6E4730697476363948536F324752336F7352474F545266687A616C6E6C4B68544C613746674B443639796A6A2F376934326630547153366F56595A747264386A6844687963657773564E6A787873386350665851664F6A2B4C636C3358796E7A69726E45437071785A484332714B305653506A452F676670796F4547756E4756723946674F64706779304359496F69315A576B3936522F717457366567336B51344C4274646962562F4676796D6C79637274356865364251
25
+ VBOX=36.00;36.00
26
+ VEN=NDKVCA
27
+ HVER=4.0
28
+ FED=57.18;57.18
@@ -0,0 +1,22 @@
1
+ body {
2
+ display: grid;
3
+ grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));
4
+ grid-gap: 1em;
5
+ }
6
+
7
+ .lenses-container {
8
+ vertical-align: top;
9
+ white-space: nowrap;
10
+ text-align: center;
11
+ }
12
+
13
+ .lens {
14
+ width: 200px;
15
+ display: inline-block;
16
+ padding: 10px;
17
+ }
18
+
19
+ .lens svg {
20
+ stroke: black;
21
+ fill: #eee;
22
+ }
@@ -0,0 +1,10 @@
1
+ require 'sinatra'
2
+ require 'lens_protocol'
3
+
4
+ get '/' do
5
+ files_and_messages = Dir['examples/oma/*.oma'].map do |file|
6
+ [File.basename(file), LensProtocol::OMA.parse(File.read(file))]
7
+ end
8
+
9
+ erb :index, locals: {files_and_messages: files_and_messages}
10
+ end
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <link rel="stylesheet" href="styles.css">
5
+ <title>SVG Demo</title>
6
+ </head>
7
+ <body>
8
+ <% files_and_messages.each do |(filename, message)| %>
9
+ <div class="lenses-container">
10
+ <div class="filename"><%= filename %></div>
11
+
12
+ <% if (svgs = message.to_svg).any? %>
13
+ <% svgs.map do |svg| %>
14
+ <div class="lens">
15
+ <%= svg %>
16
+ </div>
17
+ <% end %>
18
+ <% else %>
19
+ <div>
20
+ Could not recognize tracing data.
21
+ </div>
22
+ <% end %>
23
+ </div>
24
+ <% end %>
25
+ </body>
26
+ </html>
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "lens_protocol/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "lens_protocol"
7
+ spec.version = LensProtocol::VERSION
8
+ spec.authors = ["Emmanuel Nicolau"]
9
+ spec.email = ["emmanicolau@gmail.com"]
10
+
11
+ spec.summary = %q{LensProtocol is a Ruby parser and builder for the OMA protocol.}
12
+ spec.description = %q{A Ruby parser and builder for the OMA protocol (a.k.a. Data Communication Standard) that was developed by the Lens Processing & Technology Division of The Vision Council for interconnection of optical laboratory equipment.}
13
+ spec.homepage = "https://github.com/eeng/lens_protocol"
14
+
15
+ # Specify which files should be added to the gem when it is released.
16
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_dependency 'activesupport', '>= 4.0'
25
+ spec.add_dependency 'nokogiri'
26
+ spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency "guard-rspec"
30
+ spec.add_development_dependency 'pry'
31
+ spec.add_development_dependency 'pry-byebug'
32
+ spec.add_development_dependency 'sinatra'
33
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'nokogiri'
4
+ require 'lens_protocol/version'
5
+ require 'lens_protocol/errors'
6
+ require 'lens_protocol/oma/record'
7
+ require 'lens_protocol/oma/message'
8
+ require 'lens_protocol/oma/type/base'
9
+ require 'lens_protocol/oma/type/text'
10
+ require 'lens_protocol/oma/type/integer'
11
+ require 'lens_protocol/oma/type/numeric'
12
+ require 'lens_protocol/oma/type/trcfmt'
13
+ require 'lens_protocol/oma/type/r'
14
+ require 'lens_protocol/oma/types'
15
+ require 'lens_protocol/oma/parser'
16
+ require 'lens_protocol/oma/formatter'
17
+ require 'lens_protocol/oma'
18
+ require 'lens_protocol/svg'
@@ -0,0 +1,10 @@
1
+ module LensProtocol
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ParsingError < Error
6
+ def initialize msg = 'Parsing failed', line = 'N/A'
7
+ super "#{msg}\n Line: #{line}"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module LensProtocol
2
+ # The API layer of the library.
3
+ module OMA
4
+ module_function
5
+
6
+ def parse *args
7
+ Parser.new.parse *args
8
+ end
9
+
10
+ def format *args
11
+ Formatter.new.format *args
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ module LensProtocol
2
+ module OMA
3
+ class Formatter
4
+ # Generates the OMA string from a Message
5
+ def format message, line_separator: "\r\n", start_of_message: '', end_of_message: '', **opts
6
+ [
7
+ start_of_message,
8
+ format_lines(message, **opts).join(line_separator),
9
+ line_separator,
10
+ end_of_message
11
+ ].join
12
+ end
13
+
14
+ def format_lines message, types: {}
15
+ types = TYPES.merge(types)
16
+ message.records.values.flat_map do |record|
17
+ types[record.label].format(record, message)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,138 @@
1
+ module LensProtocol
2
+ module OMA
3
+ class Message
4
+ attr_reader :records
5
+
6
+ # Builds a message from a hash of record labels to record value.
7
+ def self.from_hash hash
8
+ hash.reduce new do |message, (label, value)|
9
+ message.add_record(label, value)
10
+ end
11
+ end
12
+
13
+ def initialize records: {}, context: {}
14
+ @records = records
15
+ @context = context
16
+ end
17
+
18
+ def add_record label, value
19
+ @records[label] ||= Record.new(label: label, value: value)
20
+ self
21
+ end
22
+
23
+ def add_record_or_insert_values label, values
24
+ @records[label] ||= Record.new(label: label, value: [])
25
+ @records[label].value << values
26
+ self
27
+ end
28
+
29
+ def add_record_or_concat_values label, values
30
+ @records[label] ||= Record.new(label: label, value: [])
31
+ @records[label].value.concat values
32
+ self
33
+ end
34
+
35
+ def add_record_side_values label, side, values
36
+ @records[label] ||= Record.new(label: label, value: [[], []])
37
+ @records[label].value[side].concat values
38
+ self
39
+ end
40
+
41
+ def set_context key, value
42
+ @context[key] = value
43
+ self
44
+ end
45
+
46
+ def value_of label, default = nil
47
+ if include? label
48
+ @records[label].value
49
+ else
50
+ default
51
+ end
52
+ end
53
+
54
+ # Returns +true+ if the message contains a record with the given label
55
+ def include? label
56
+ @records.key? label
57
+ end
58
+
59
+ def empty?
60
+ @records.empty?
61
+ end
62
+
63
+ def context key
64
+ @context[key]
65
+ end
66
+
67
+ def to_hash
68
+ Hash[*@records.flat_map { |label, record| [label, record.value] }]
69
+ end
70
+
71
+ # Returns the "R" reconds decoded radiuses according to the tracing format.
72
+ def radius_data
73
+ return [] unless value_of('TRCFMT') && value_of('R')
74
+ [0, 1].map do |side|
75
+ format_number, = value_of('TRCFMT')[side]
76
+ case format_number.to_i
77
+ when 0 # side not present
78
+ []
79
+ when 1 # ASCII format
80
+ value_of('R')[side]
81
+ else # unknown format
82
+ return []
83
+ end
84
+ end
85
+ end
86
+
87
+ # Converts the "R" record values to polar coordinates.
88
+ def tracing_in_polar_coordinates
89
+ radius_data.map { |radiuses| radiuses_to_polar radiuses }
90
+ end
91
+
92
+ def radiuses_to_polar radiuses
93
+ radiuses.map.with_index { |r, i| [i * 2 * Math::PI / radiuses.size, r] }
94
+ end
95
+
96
+ # Converts the "R" record values to rectangular coordinates.
97
+ def tracing_in_rectangular_coordinates
98
+ radius_data.map { |radiuses| radiuses_to_rectangular radiuses }
99
+ end
100
+
101
+ def radiuses_to_rectangular radiuses
102
+ radiuses_to_polar(radiuses).map { |(a, r)| [r * Math.cos(a), r * Math.sin(a)].map { |v| v.round 2 } }
103
+ end
104
+
105
+ # Returns an array of SVG strings, one for each side. If the tracing format is not recognized
106
+ # or there is no tracing data, returns an empty array.
107
+ def to_svg **opts
108
+ SVG.from_message self, **opts
109
+ end
110
+
111
+ # Similarly to +Hash#merge+ returns a new message containing the records of +this+ and the records of +other+
112
+ # keeping the ones in +other+ if the labels colides.
113
+ def merge other
114
+ Message.new records: @records.merge(other.records)
115
+ end
116
+
117
+ # Returns a new message without the records of the given labels.
118
+ # @param labels [Array]
119
+ def except labels
120
+ Message.new records: @records.reject { |label, _| labels.include? label }
121
+ end
122
+
123
+ # Returns a new message with only the records of the given labels.
124
+ # @param labels [Array]
125
+ def only labels
126
+ Message.new records: @records.slice(*labels)
127
+ end
128
+
129
+ def remove_empty_records
130
+ Message.new records: @records.reject { |_, r| r.empty? }
131
+ end
132
+
133
+ def to_s
134
+ OMA.format self
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,24 @@
1
+ module LensProtocol
2
+ module OMA
3
+ class Parser
4
+ def parse oma_str, types: {}
5
+ types = TYPES.merge(types)
6
+ normalize_line_endings(oma_str)
7
+ .split("\n")
8
+ .reduce(Message.new) { |message, line| parse_line line, message, types }
9
+ end
10
+
11
+ private
12
+
13
+ def parse_line line, message, types
14
+ raise ParsingError.new('The label separator is missing', line) unless line.include?('=')
15
+ label, = line.split('=')
16
+ types[label].parse(line, message)
17
+ end
18
+
19
+ def normalize_line_endings str
20
+ str.to_s.gsub /\r\n?/, "\n"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module LensProtocol
2
+ module OMA
3
+ class Record
4
+ attr_reader :label
5
+
6
+ # May hold a single value, an array of values (on multi-value and chiral records), or an array of array of values (in R records for example)
7
+ attr_reader :value
8
+
9
+ def initialize label:, value:
10
+ @label = label
11
+ @value = value
12
+ end
13
+
14
+ def empty?
15
+ Array(value).select(&:present?).empty?
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module LensProtocol
2
+ module OMA
3
+ module Type
4
+ class Base
5
+ def initialize mode: :single_value
6
+ @mode = mode
7
+ end
8
+
9
+ # Given a line and a message produces a new message with the record(s) corresponding to that line added to the message
10
+ # @return [Message]
11
+ def parse line, message
12
+ label, data = label_and_data line
13
+ case @mode
14
+ when :single_value
15
+ message.add_record label, parse_value(data)
16
+ when :array_of_values
17
+ message.add_record_or_concat_values label, parse_values(data)
18
+ when :chiral
19
+ message.add_record label, parse_chiral(data)
20
+ when :matrix_of_values
21
+ message.add_record_or_insert_values label, parse_values(data)
22
+ else
23
+ raise ArgumentError, "Mode #{@mode} not supported"
24
+ end
25
+ end
26
+
27
+ # @return [Array of lines or a single one]
28
+ def format record, _message
29
+ case @mode
30
+ when :single_value
31
+ format_line record.label, [format_value(record.value)]
32
+ when :array_of_values
33
+ format_line record.label, format_values(record.value)
34
+ when :chiral
35
+ format_line record.label, format_chiral(record.value)
36
+ when :matrix_of_values
37
+ record.value.map do |value|
38
+ format_line record.label, format_values(value)
39
+ end
40
+ else
41
+ raise ArgumentError, "Mode #{@mode} not supported"
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def label_and_data line
48
+ label, data = line.split('=', -1)
49
+ [label, data]
50
+ end
51
+
52
+ def label_and_values line
53
+ label, data = label_and_data line
54
+ [label, parse_values(data)]
55
+ end
56
+
57
+ def parse_values data
58
+ data.split(';', -1).map { |value| parse_value value }
59
+ end
60
+
61
+ def parse_value value
62
+ value if value != '?'
63
+ end
64
+
65
+ def parse_chiral values
66
+ make_chiral parse_values values
67
+ end
68
+
69
+ def make_chiral values
70
+ if values.size <= 1
71
+ [values[0], values[0]]
72
+ else
73
+ values[0..1]
74
+ end
75
+ end
76
+
77
+ def format_line label, values
78
+ "#{label}=#{values.join(';')}"
79
+ end
80
+
81
+ def format_value value
82
+ value
83
+ end
84
+
85
+ def format_values values
86
+ values = values.is_a?(Array) ? values : [values]
87
+ values.map { |v| format_value(v) }
88
+ end
89
+
90
+ def format_chiral value
91
+ return [] if Array(value).select(&:present?).empty?
92
+ make_chiral format_values value
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end