vizcore 1.0.0 → 1.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.
@@ -19,6 +19,7 @@ module Vizcore
19
19
  SUPPORTED_BLEND_MODES = Vizcore::LayerCatalog::BLEND_MODES
20
20
  SUPPORTED_POST_EFFECTS = Vizcore::LayerCatalog::POST_EFFECTS
21
21
  SUPPORTED_VJ_EFFECTS = Vizcore::LayerCatalog::VJ_EFFECTS
22
+ SUPPORTED_SHAPE_KINDS = %i[circle line rect polygon polyline path star].freeze
22
23
 
23
24
  Issue = Struct.new(:severity, :message, keyword_init: true) do
24
25
  def error?
@@ -113,6 +114,7 @@ module Vizcore
113
114
  issues << warn("scene #{scene_name} layer #{layer_name} has an empty GLSL file") if layer[:glsl] && glsl_source.to_s.empty?
114
115
  validate_blend_mode(layer, scene_name, layer_name, issues)
115
116
  validate_layer_effects(layer, scene_name, layer_name, issues)
117
+ validate_shape_layer(layer, scene_name, layer_name, issues)
116
118
  validate_mappings(Array(layer[:mappings]), scene_name, layer_name, issues)
117
119
  end
118
120
 
@@ -130,10 +132,100 @@ module Vizcore
130
132
  validate_effect_name(params[:vj_effect], SUPPORTED_VJ_EFFECTS, "vj_effect", scene_name, layer_name, issues)
131
133
  end
132
134
 
135
+ def validate_shape_layer(layer, scene_name, layer_name, issues)
136
+ params = layer[:params] || {}
137
+ return unless shape_layer?(layer) || shape_value(params, :shapes)
138
+
139
+ Array(shape_value(params, :shapes)).each_with_index do |shape, index|
140
+ values = shape_hash(shape)
141
+ label = shape_label(values, index)
142
+ validate_shape_kind(values, label, scene_name, layer_name, issues)
143
+ validate_shape_fallback_fill(values, label, scene_name, layer_name, issues)
144
+ validate_shape_opacity(values, label, scene_name, layer_name, issues)
145
+ validate_shape_scale(values, label, scene_name, layer_name, issues)
146
+ end
147
+ end
148
+
149
+ def validate_shape_kind(shape, label, scene_name, layer_name, issues)
150
+ kind = shape_value(shape, :kind)&.to_sym
151
+ return if SUPPORTED_SHAPE_KINDS.include?(kind)
152
+
153
+ issues << warn("scene #{scene_name} layer #{layer_name} shape #{label} uses unsupported kind: #{kind || "missing"}")
154
+ end
155
+
156
+ def validate_shape_fallback_fill(shape, label, scene_name, layer_name, issues)
157
+ fill = shape_value(shape, :fill)
158
+ return if fill.nil? || fill.to_s.empty?
159
+
160
+ issues << warn("scene #{scene_name} layer #{layer_name} shape #{label} fill may be ignored by line fallback")
161
+ end
162
+
163
+ def validate_shape_opacity(shape, label, scene_name, layer_name, issues)
164
+ opacity = numeric_shape_value(shape_value(shape, :opacity))
165
+ return unless opacity && (opacity.negative? || opacity > 1)
166
+
167
+ issues << warn("scene #{scene_name} layer #{layer_name} shape #{label} opacity #{opacity} is outside 0..1; renderer will clamp")
168
+ end
169
+
170
+ def validate_shape_scale(shape, label, scene_name, layer_name, issues)
171
+ scale_values(shape).each do |scale|
172
+ next unless scale
173
+
174
+ if scale.zero?
175
+ issues << warn("scene #{scene_name} layer #{layer_name} shape #{label} scale includes 0; shape may collapse")
176
+ elsif scale.abs > 8
177
+ issues << warn("scene #{scene_name} layer #{layer_name} shape #{label} scale #{scale} is extreme; renderer will clamp")
178
+ end
179
+ end
180
+ end
181
+
133
182
  def supported_layer_types
134
183
  Vizcore::LayerCatalog.supported_types
135
184
  end
136
185
 
186
+ def shape_layer?(layer)
187
+ %w[shape shapes shape_layer].include?((layer[:type] || layer["type"]).to_s)
188
+ end
189
+
190
+ def shape_label(shape, index)
191
+ id = shape_value(shape, :id)
192
+ id ? "`#{id}`" : "##{index + 1}"
193
+ end
194
+
195
+ def scale_values(shape)
196
+ transform = Hash(shape_value(shape, :transform) || {})
197
+ scale = shape_value(transform, :scale) || shape_value(shape, :scale)
198
+ case scale
199
+ when Hash
200
+ [numeric_shape_value(shape_value(scale, :x)), numeric_shape_value(shape_value(scale, :y))]
201
+ when Array
202
+ [numeric_shape_value(scale[0]), numeric_shape_value(scale[1])]
203
+ else
204
+ [numeric_shape_value(scale)]
205
+ end
206
+ rescue TypeError
207
+ []
208
+ end
209
+
210
+ def numeric_shape_value(value)
211
+ return nil if value.nil?
212
+
213
+ numeric = Float(value)
214
+ numeric if numeric.finite?
215
+ rescue ArgumentError, TypeError
216
+ nil
217
+ end
218
+
219
+ def shape_hash(value)
220
+ Hash(value)
221
+ rescue TypeError
222
+ {}
223
+ end
224
+
225
+ def shape_value(hash, key)
226
+ hash[key] || hash[key.to_s]
227
+ end
228
+
137
229
  def validate_effect_name(value, supported, field, scene_name, layer_name, issues)
138
230
  return unless value
139
231
  return if supported.include?(value.to_sym)