Class: Oozby::Preprocessor

Inherits:
Object
  • Object
show all
Defined in:
lib/oozby/preprocessor.rb,
lib/oozby/preprocessor-definitions.rb

Overview

The Oozby Method Preprocessor handles requests via the transform_call method and transforms the Oozby::Element passed in, patching in any extra features and trying to alert the user of obvious bugs

Constant Summary collapse

NoResolution =

never pass resolution data in to these methods - it’s pointless:

%i(translate rotate scale mirror resize difference union
intersection hull minkowski color)
DefaultOpenSCADMethods =

list of OpenSCAD standard methods - these can pass through without error:

%i(
  cube sphere cylinder polyhedron
  circle square polygon
  scale resize rotate translate mirror multmatrix color minkowski hull
  linear_extrude rotate_extrude import import_dxf projection
  union difference intersection render
  echo
)
@@system_methods =

remember list of public methods defined so far - these are system ones

public_instance_methods(false)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env: nil, ooz: nil) ⇒ Preprocessor

setup a new method preprocessor



72
73
74
75
76
# File 'lib/oozby/preprocessor.rb', line 72

def initialize env: nil, ooz: nil
  @env = env
  @parent = ooz
  @openscad_methods = DefaultOpenSCADMethods.dup
end

Instance Attribute Details

#callObject

Returns the value of attribute call.



70
71
72
# File 'lib/oozby/preprocessor.rb', line 70

def call
  @call
end

#openscad_methodsObject

Returns the value of attribute openscad_methods.



70
71
72
# File 'lib/oozby/preprocessor.rb', line 70

def openscad_methods
  @openscad_methods
end

Class Method Details

.default_filters(*list) ⇒ Object

sets a list of default filters which aren’t reset after each method def



11
12
13
14
# File 'lib/oozby/preprocessor.rb', line 11

def default_filters *list
  @@default_filters = list.map { |x| if x.is_a? Array then x else [x] end }
  @@queued_filters = @@default_filters.dup
end

.filter(filter_name, *options) ⇒ Object

set a filter to be added only to the next method def



17
18
19
20
21
# File 'lib/oozby/preprocessor.rb', line 17

def filter filter_name, *options
  @@queued_filters ||= @@default_filters.dup
  @@queued_filters.delete_if { |x| x[0] == filter_name } # replace default definitions
  @@queued_filters.push([filter_name, *options]) # add new filter definition
end

.filters_for(method_name) ⇒ Object

get list of filters for a method name



41
42
43
# File 'lib/oozby/preprocessor.rb', line 41

def filters_for method_name
  @@method_filters[method_name] || []
end

.finalize_filter(method_name) ⇒ Object



29
30
31
32
# File 'lib/oozby/preprocessor.rb', line 29

def finalize_filter method_name
  @@method_filters[method_name] = @@queued_filters
  @@queued_filters = @@default_filters.dup
end

.method_added(method_name) ⇒ Object

detect a method def, store it’s filters and reset for next def



24
25
26
27
# File 'lib/oozby/preprocessor.rb', line 24

def method_added method_name
  finalize_filter method_name
  super
end

.oozby_alias(from, to, **extra_args) ⇒ Object

alias an name to an openscad method optionally with extra defaults useful for giving things more descriptive names, where those names imply different defaults, like hexagon -> circle(sides: 6)



48
49
50
51
52
53
54
# File 'lib/oozby/preprocessor.rb', line 48

def oozby_alias from, to, **extra_args
  define_method(from) do
    call.named_args.merge!(extra_args) #{ |key,l,r| l } # left op wins conflicts
    run_filters to
    redirect to
  end
end

.passthrough(method_name, *arg_names) ⇒ Object

don’t want to define a primary processor method? pass it through manually



35
36
37
38
# File 'lib/oozby/preprocessor.rb', line 35

def passthrough method_name, *arg_names
  arg_list = arg_names.map { |x| "#{x}: nil" }.join(', ')
  define_method method_name, &eval("->(#{arg_list}) {nil}")
end

Instance Method Details

#args_parse(info, *arg_names) ⇒ Object

parse arguments like openscad does



155
156
157
158
159
160
161
162
163
# File 'lib/oozby/preprocessor.rb', line 155

def args_parse(info, *arg_names)
  args = info.named_args.dup
  info.args.length.times do |index|
    warn "Overwriting argument #{arg_names[index]}" if args.key? arg_names[index]
    args[arg_names[index]] = info.args[index]
  end
  
  args
end

#capture(&proc) ⇒ Object

capture contents of a block as openscad code, returning AST array



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/oozby/preprocessor.rb', line 166

def capture &proc
  env = @env
  (env._subscope {
    env.preprocessor(false) {
      env._execute_oozby(&proc)
    }
  }).find { |x|
    x.is_a? Oozby::Element
  }.tap { |x|
    x.modifier = "#{call.modifier}#{x.modifier}" if call.modifier
  }
end

#cube(size: [1,1,1], center: false, corner_radius: 0) ⇒ Object



25
26
27
28
# File 'lib/oozby/preprocessor-definitions.rb', line 25

def cube size: [1,1,1], center: false, corner_radius: 0
  return rounded_rectangular_prism(size: size, center: center, corner_radius: corner_radius) if corner_radius > 0
  return call
end

#cylinder(h: 1, r1: nil, r2: nil, r: nil, center: false, corner_radius: 0) ⇒ Object



34
35
36
37
38
# File 'lib/oozby/preprocessor-definitions.rb', line 34

def cylinder h: 1, r1: nil, r2: nil, r: nil, center: false, corner_radius: 0
  r1, r2 = r, r if r unless r1 || r2
  return rounded_cylinder(h: h, r1: r1, r2: r2, center: center, corner_radius: corner_radius) if corner_radius > 0
  return call
end

#knownObject

array of all known method names



140
141
142
143
144
145
# File 'lib/oozby/preprocessor.rb', line 140

def known
  list = @openscad_methods.dup
  list.push *primary_processors
  list.push *@@method_filters.keys
  list.uniq
end

#known?(name) ⇒ Boolean

does this processor know of a method named whatever?

Returns:

  • (Boolean)


135
136
137
# File 'lib/oozby/preprocessor.rb', line 135

def known? name
  known.include? name.to_sym
end

#primary_method_argsObject

generates argument list to call a primary method processor



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/oozby/preprocessor.rb', line 114

def primary_method_args # :nodoc:
  # grab the primary processor
  primary = self.class.public_instance_method(call.method.to_sym)
  params = primary.parameters
  # parse the processor method's signature
  param_names = params.select { |x| x.first == :key }.map { |x| x[1] }
  # filter down args to only those requested
  calling_args = call.named_args.dup
  # convert unnamed call args to named args
  calling_args.merge! args_parse(call, *param_names)
  # delete any args the receiver can't handle
  calling_args.delete_if { |k,v| not param_names.include?(k) }
  if calling_args.empty? then [] else [calling_args] end
end

#primary_processorsObject

list of primary processor methods



130
131
132
# File 'lib/oozby/preprocessor.rb', line 130

def primary_processors
  @primary_processors ||= public_methods(false) - @@system_methods
end

#redirect(new_method) ⇒ Object

rewrite this method to a different method name and primary processor and whatever else



148
149
150
151
# File 'lib/oozby/preprocessor.rb', line 148

def redirect new_method
  call.method = new_method.to_sym
  public_send(new_method, *primary_method_args) if self.respond_to? new_method
end

#run_filters(method_name) ⇒ Object



104
105
106
107
108
109
110
111
# File 'lib/oozby/preprocessor.rb', line 104

def run_filters method_name
  # apply the other filters
  filters = self.class.filters_for(method_name)
  filters.each do |filter_data|
    filter_name, *filter_args = filter_data
    send(filter_name, *filter_args)
  end
end

#square(size: [1,1], center: false, corner_radius: 0) ⇒ Object



48
49
50
51
# File 'lib/oozby/preprocessor-definitions.rb', line 48

def square size: [1,1], center: false, corner_radius: 0
  return rounded_rectangle(size: size, center: center, corner_radius: corner_radius) if corner_radius > 0
  return call
end

#transform_call(call_info) ⇒ Object

accepts an Oozby::Element and transforms it according to the processors’ rules



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/oozby/preprocessor.rb', line 79

def transform_call call_info
  raise "call info isn't Oozby::Element #{call_info.inspect}" unless call_info.is_a? Oozby::Element
  @call = call_info
  @original_method = @call.method
  
  run_filters call_info.method.to_sym
  
  methods = primary_processors
  # if a primary processor is defined for this kind of call
  if methods.include? call_info.method.to_sym
    # call the primary processor
    result = public_send(call_info.method, *primary_method_args)
    
    # replace the ast content with the processor's output
    if result.is_a? Hash or result.is_a? Oozby::Element
      # replace called item with this new stuff
      return result
    elsif result != nil # ignore nil - we don't need to do anything for that!
      raise "#{@original_method} preprocessor returned invalid result #{result.inspect}"
    end
  end
  
  return call_info
end