lens_protocol 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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