ADG Lua 

ADG Lua Git Source Tree

Root/piston.lua

1--[[
2
3This file is part of adg-lua.
4Copyright (C) 2012-2013 Nicola Fontana <ntd at entidi.it>
5
6adg-lua is free software; you can redistribute it and/or modify
7it under the terms of the GNU Lesser General Public License as
8published by the Free Software Foundation; either version 2 of
9the License, or (at your option) any later version.
10
11adg-lua is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU Lesser General Public License for more details.
15
16You should have received a copy of the GNU Lesser General
17Public License along with adg-lua; if not, write to the Free
18Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19Boston, MA 02110-1301, USA.
20
21]]
22
23local lgi = require 'lgi'
24local cairo = lgi.require 'cairo'
25local Cpml = lgi.require 'Cpml'
26local Adg = lgi.require 'Adg'
27
28local SQRT3 = math.sqrt(3)
29local generator = {}
30
31
32-- Backward compatibility
33
34if not cairo.Status.to_string then
35 -- Pull request: http://github.com/pavouk/lgi/pull/44
36 local core = require 'lgi.core'
37 local ffi = require 'lgi.ffi'
38 local ti = ffi.types
39
40 cairo._enum.Status.to_string = core.callable.new {
41addr = cairo._module.cairo_status_to_string,
42ret = ti.utf8,
43cairo.Status
44 }
45end
46
47
48-- MODEL
49-----------------------------------------------------------------
50
51generator.model = {}
52local constructor = {}
53
54-- Inject the regenerate method into Adg.Model
55--
56-- Rebuilding the model *without* destroying it is the quickest method
57-- to change a drawing: the notification mechanism will change only the
58-- entities that effectively need to be modified.
59--
60-- Another (easier) option would be to regenerate everything - that is
61-- models and views - from scratch.
62rawset(Adg.Model, 'regenerate', function (model, part)
63 -- Call the original constructor of model, registered during the first call
64 -- to the same constructor, to regenerate it with the data stored in part.
65 constructor[model](part, model)
66end)
67
68function generator.model.hole(part, path)
69 path = path or Adg.Path {}
70 constructor[path] = generator.model.hole
71
72 local data = part.data
73
74 local pair = Cpml.Pair { x = data.LHOLE, y = 0 }
75 path:move_to(pair)
76 path:set_named_pair('LHOLE', pair)
77
78 pair.y = data.DHOLE / 2
79 pair.x = pair.x - pair.y / SQRT3
80 path:line_to(pair)
81 local edge = pair:dup()
82
83 pair.x = 0
84 path:line_to(pair)
85 path:set_named_pair('DHOLE', pair)
86
87 path:line_to_explicit(0, (data.D1 + data.DHOLE) / 4)
88 path:curve_to_explicit(data.LHOLE / 2, data.DHOLE / 2,
89 data.LHOLE + 2, data.D1 / 2,
90 data.LHOLE + 2, 0)
91 path:reflect()
92 path:close()
93
94 -- No need to incomodate an AdgEdge model for two reasons:
95 -- it is only a single line and it is always needed
96 path:move_to(edge)
97 edge.y = -edge.y
98 path:line_to(edge)
99
100 return path
101end
102
103local function add_groove(path, part)
104 local data = part.data
105 local pair = Cpml.Pair { x = data.ZGROOVE, y = data.D1 / 2 }
106
107 path:line_to(pair)
108 path:set_named_pair('DGROOVEI_X', pair)
109
110 pair.y = data.D3 / 2
111 path:set_named_pair('DGROOVEY_POS', pair)
112
113 pair.y = data.DGROOVE / 2
114 path:line_to(pair)
115 path:set_named_pair('DGROOVEI_Y', pair)
116
117 pair.x = pair.x + data.LGROOVE
118 path:line_to(pair)
119
120 pair.y = data.D3 / 2
121 path:set_named_pair('DGROOVEX_POS', pair)
122
123 pair.y = data.D1 / 2
124 path:line_to(pair)
125 path:set_named_pair('DGROOVEF_X', pair)
126end
127
128function generator.model.body(part, path)
129 path = path or Adg.Path {}
130 constructor[path] = generator.model.body
131
132 local data = part.data
133
134 local pair = Cpml.Pair { x = 0, y = data.D1 / 2 }
135 path:move_to(pair)
136 path:set_named_pair('D1I', pair)
137
138 if data.GROOVE then add_groove(path, part) end
139
140 pair.x = data.A - data.B - data.LD2
141 path:line_to(pair)
142
143 pair.y = data.D3 / 2
144 path:set_named_pair('D2_POS', pair)
145
146 pair.x = pair.x + (data.D1 - data.D2) / 2
147 pair.y = data.D2 / 2
148 path:line_to(pair)
149 path:set_named_pair('D2I', pair)
150
151 pair.x = data.A - data.B
152 path:line_to(pair)
153 path:fillet(0.4)
154
155 pair.x = data.A - data.B
156 pair.y = data.D3 / 2
157 path:line_to(pair)
158 path:set_named_pair('D3I', pair)
159
160 pair.x = data.A
161 path:set_named_pair('East', pair)
162
163 pair.x = 0
164 path:set_named_pair('West', pair)
165
166 path:chamfer(data.CHAMFER, data.CHAMFER)
167
168 pair.x = data.A - data.B + data.LD3
169 pair.y = data.D3 / 2
170 path:line_to(pair)
171
172 local primitive = path:over_primitive()
173 local tmp = primitive:put_point(0)
174 path:set_named_pair('D3I_X', tmp)
175
176 tmp = primitive:put_point(-1)
177 path:set_named_pair('D3I_Y', tmp)
178
179 path:chamfer(data.CHAMFER, data.CHAMFER)
180
181 pair.y = data.D4 / 2
182 path:line_to(pair)
183
184 primitive = path:over_primitive()
185 tmp = primitive:put_point(0)
186 path:set_named_pair('D3F_Y', tmp)
187 tmp = primitive:put_point(-1)
188 path:set_named_pair('D3F_X', tmp)
189
190 path:fillet(data.RD34)
191
192 pair.x = pair.x + data.RD34
193 path:set_named_pair('D4I', pair)
194
195 pair.x = data.A - data.C - data.LD5
196 path:line_to(pair)
197 path:set_named_pair('D4F', pair)
198
199 pair.y = data.D3 / 2
200 path:set_named_pair('D4_POS', pair)
201
202 primitive = path:over_primitive()
203 tmp = primitive:put_point(0)
204 tmp.x = tmp.x + data.RD34
205 path:set_named_pair('RD34', tmp)
206
207 tmp.x = tmp.x - math.cos(math.pi / 4) * data.RD34
208 tmp.y = tmp.y - math.sin(math.pi / 4) * data.RD34
209 path:set_named_pair('RD34_R', tmp)
210
211 tmp.x = tmp.x + data.RD34
212 tmp.y = tmp.y + data.RD34
213 path:set_named_pair('RD34_XY', tmp)
214
215 pair.x = pair.x + (data.D4 - data.D5) / 2
216 pair.y = data.D5 / 2
217 path:line_to(pair)
218 path:set_named_pair('D5I', pair)
219
220 pair.x = data.A - data.C
221 path:line_to(pair)
222
223 path:fillet(0.2)
224
225 pair.y = data.D6 / 2
226 path:line_to(pair)
227
228 primitive = path:over_primitive()
229 tmp = primitive:put_point(0)
230 path:set_named_pair('D5F', tmp)
231
232 path:fillet(0.1)
233
234 pair.x = pair.x + data.LD6
235 path:line_to(pair)
236 path:set_named_pair('D6F', pair)
237
238 primitive = path:over_primitive()
239 tmp = primitive:put_point(0)
240 path:set_named_pair('D6I_X', tmp)
241
242 primitive = path:over_primitive()
243 tmp = primitive:put_point(-1)
244 path:set_named_pair('D6I_Y', tmp)
245
246 pair.x = data.A - data.LD7
247 pair.y = pair.y - (data.C - data.LD7 - data.LD6) / SQRT3
248 path:line_to(pair)
249 path:set_named_pair('D67', pair)
250
251 pair.y = data.D7 / 2
252 path:line_to(pair)
253
254 pair.x = data.A
255 path:line_to(pair)
256 path:set_named_pair('D7F', pair)
257
258 path:reflect_explicit(1, 0)
259 path:close()
260
261 return path
262end
263
264function generator.model.edges(part, edges)
265 edges = edges or Adg.Edges {}
266 constructor[edges] = generator.model.edges
267
268 edges:set_source(part.model.body)
269
270 return edges
271end
272
273function generator.model.axis(part, path)
274 --[[
275XXX: actually the end points can extend outside the body
276only in local space. The proper extension values should be
277expressed in global space but actually is impossible to
278combine local and global space in the AdgPath API.
279 --]]
280 path = path or Adg.Path {}
281 constructor[path] = generator.model.axis
282
283 local data = part.data
284
285 path:move_to_explicit(-1, 0)
286 path:line_to_explicit(data.A + 1, 0)
287
288 return path
289end
290
291
292-- VIEW
293-----------------------------------------------------------------
294
295generator.view = {}
296
297-- Inject the export method into Adg.Canvas
298rawset(Adg.Canvas, 'export', function (canvas, file, format)
299 -- The not explicitely set, the export format is guessed from the file suffix
300 if not format then format = file:match('%.([^.]+)$') end
301
302 local size = canvas:get_size():dup()
303 size:transform(canvas:get_global_map())
304
305 -- Create the cairo surface
306 local surface
307 if format == 'png' and cairo.ImageSurface then
308surface = cairo.ImageSurface.create(cairo.Format.RGB24, size.x, size.y)
309 elseif format == 'svg' and cairo.SvgSurface then
310surface = cairo.SvgSurface.create(file, size.x, size.y)
311 elseif format == 'pdf' and cairo.PdfSurface then
312surface = cairo.PdfSurface.create(file, size.x, size.y)
313 elseif format == 'ps' and cairo.PsSurface then
314-- Pull request: http://github.com/pavouk/lgi/pull/46
315surface = cairo.PsSurface.create(file, size.x, size.y)
316surface:dsc_comment('%%Title: adg-lua demonstration program')
317surface:dsc_comment('%%Copyright: Copyleft (C) 2013 Fontana Nicola')
318surface:dsc_comment('%%Orientation: Portrait')
319surface:dsc_begin_setup()
320surface:dsc_begin_page_setup()
321surface:dsc_comment('%%IncludeFeature: *PageSize A4')
322 elseif not format then
323format = '<nil>'
324 end
325 if not surface then
326return nil, 'Requested format not supported (' .. format .. ')'
327 end
328
329 -- Render the canvas content
330 local cr = cairo.Context.create(surface)
331 canvas:render(cr)
332 local status
333
334 if cairo.Surface.get_type(surface) == 'IMAGE' then
335status = cairo.Surface.write_to_png(surface, file)
336 else
337cr:show_page()
338status = cr.status
339 end
340
341 if status ~= 'SUCCESS' then
342return nil, cairo.Status.to_string(cairo.Status[status])
343 end
344end)
345
346local function add_title_block(canvas)
347 canvas:set_title_block(Adg.TitleBlock {
348title = '',
349author = '',
350date = '',
351drawing = '',
352logo = Adg.Logo {},
353projection = Adg.Projection { scheme = Adg.ProjectionScheme.FIRST_ANGLE },
354size = 'A4',
355 })
356end
357
358local function add_dimensions(canvas, model)
359 local body = model.body
360 local hole = model.hole
361 local dim
362
363
364 -- North
365
366 dim = Adg.LDim.new_full_from_model(body, '-D3I_X', '-D3F_X', '-D3F_Y', -math.pi/2)
367 dim:set_outside(Adg.ThreeState.OFF)
368 canvas:add(dim)
369
370 dim = Adg.LDim.new_full_from_model(body, '-D6I_X', '-D67', '-East', -math.pi/2)
371 dim:set_level(0)
372 dim:switch_extension1(false)
373 canvas:add(dim)
374
375 dim = Adg.LDim.new_full_from_model(body, '-D6I_X', '-D7F', '-East', -math.pi/2)
376 dim:set_limits('-0.06', nil)
377 canvas:add(dim)
378
379 dim = Adg.ADim.new_full_from_model(body, '-D6I_Y', '-D6F', '-D6F', '-D67', '-D6F')
380 dim:set_level(2)
381 canvas:add(dim)
382
383 dim = Adg.RDim.new_full_from_model(body, '-RD34', '-RD34_R', '-RD34_XY')
384 canvas:add(dim)
385
386 dim = Adg.LDim.new_full_from_model(body, '-DGROOVEI_X', '-DGROOVEF_X', '-DGROOVEX_POS', -math.pi/2)
387 canvas:add(dim)
388
389 dim = Adg.LDim.new_full_from_model(body, 'D2I', '-D2I', '-D2_POS', math.pi)
390 dim:set_limits('-0.1', nil)
391 dim:set_outside(Adg.ThreeState.OFF)
392 dim:set_value('\226\140\128 <>')
393 canvas:add(dim)
394
395 dim = Adg.LDim.new_full_from_model(body, 'DGROOVEI_Y', '-DGROOVEI_Y', '-DGROOVEY_POS', math.pi)
396 dim:set_limits('-0.1', nil)
397 dim:set_outside(Adg.ThreeState.OFF)
398 dim:set_value('\226\140\128 <>')
399 canvas:add(dim)
400
401
402 -- South
403
404 dim = Adg.ADim.new_full_from_model(body, 'D1F', 'D1I', 'D2I', 'D1F', 'D1F')
405 dim:set_level(2)
406 dim:switch_extension2(false)
407 canvas:add(dim)
408
409 dim = Adg.LDim.new_full_from_model(body, 'D1I', nil, 'West', math.pi / 2)
410 dim:set_ref2_from_model(hole, '-LHOLE')
411 dim:switch_extension1(false)
412 canvas:add(dim)
413
414 dim = Adg.LDim.new_full_from_model(body, 'D1I', 'DGROOVEI_X', 'West', math.pi / 2)
415 dim:switch_extension1(false)
416 dim:set_level(2)
417 canvas:add(dim)
418
419 dim = Adg.LDim.new_full_from_model(body, 'D4F', 'D6I_X', 'D4_POS', math.pi / 2)
420 dim:set_limits(nil, '+0.2')
421 dim:set_outside(Adg.ThreeState.OFF)
422 canvas:add(dim)
423
424 dim = Adg.LDim.new_full_from_model(body, 'D1F', 'D3I_X', 'D2_POS', math.pi / 2)
425 dim:set_level(2)
426 dim:switch_extension2(false)
427 dim:set_outside(Adg.ThreeState.OFF)
428 canvas:add(dim)
429
430 dim = Adg.LDim.new_full_from_model(body, 'D3I_X', 'D7F', 'East', math.pi / 2)
431 dim:set_limits(nil, '+0.1')
432 dim:set_level(2)
433 dim:set_outside(Adg.ThreeState.OFF)
434 dim:switch_extension2(false)
435 canvas:add(dim)
436
437 dim = Adg.LDim.new_full_from_model(body, 'D1I', 'D7F', 'D3F_Y', math.pi / 2)
438 dim:set_limits('-0.05', '+0.05')
439 dim:set_level(3)
440 canvas:add(dim)
441
442 dim = Adg.ADim.new_full_from_model(body, 'D4F', 'D4I', 'D5I', 'D4F', 'D4F')
443 dim:set_level(1.5)
444 dim:switch_extension2(false)
445 canvas:add(dim)
446
447
448 -- East
449
450 dim = Adg.LDim.new_full_from_model(body, 'D6F', '-D6F', 'East', 0)
451 dim:set_limits('-0.1', nil)
452 dim:set_level(4)
453 dim:set_value('\226\140\128 <>')
454 canvas:add(dim)
455
456 dim = Adg.LDim.new_full_from_model(body, 'D4F', '-D4F', 'East', 0)
457 dim:set_level(3)
458 dim:set_value('\226\140\128 <>')
459 canvas:add(dim)
460
461 dim = Adg.LDim.new_full_from_model(body, 'D5F', '-D5F', 'East', 0)
462 dim:set_limits('-0.1', nil)
463 dim:set_level(2)
464 dim:set_value('\226\140\128 <>')
465 canvas:add(dim)
466
467 dim = Adg.LDim.new_full_from_model(body, 'D7F', '-D7F', 'East', 0)
468 dim:set_value('\226\140\128 <>')
469 canvas:add(dim)
470
471
472 -- West
473
474 dim = Adg.LDim.new_full_from_model(hole, 'DHOLE', '-DHOLE', nil, math.pi)
475 dim:set_pos_from_model(body, '-West')
476 dim:set_value('\226\140\128 <>')
477 canvas:add(dim)
478
479 dim = Adg.LDim.new_full_from_model(body, 'D1I', '-D1I', '-West', math.pi)
480 dim:set_limits('-0.05', '+0.05')
481 dim:set_level(2)
482 dim:set_value('\226\140\128 <>')
483 canvas:add(dim)
484
485 dim = Adg.LDim.new_full_from_model(body, 'D3I_Y', '-D3I_Y', '-West', math.pi)
486 dim:set_limits('-0.25', nil)
487 dim:set_level(3)
488 dim:set_value('\226\140\128 <>')
489 canvas:add(dim)
490end
491
492function generator.view.detailed(part)
493 local canvas = Adg.Canvas {}
494 local model = part.model
495
496 add_title_block(canvas)
497 canvas:add(Adg.Stroke { trail = model.body })
498 canvas:add(Adg.Stroke { trail = model.edges })
499 canvas:add(Adg.Hatch { trail = model.hole })
500 canvas:add(Adg.Stroke { trail = model.hole })
501 canvas:add(Adg.Stroke {
502trail = model.axis,
503line_dress = Adg.Dress.LINE_AXIS
504 })
505 add_dimensions(canvas, model)
506
507 return canvas
508end
509
510
511-- CONTROLLER
512-----------------------------------------------------------------
513
514local controller = {}
515
516function controller.new(data)
517 local part = {}
518
519 local function generate(class, method)
520local constructor = generator[class][method]
521local result = constructor and constructor(part) or false
522part[class][method] = result
523return result
524 end
525
526 -- data: numbers and strings needed to define the whole part
527 part.data = data or {}
528
529 -- model: different models (AdgModel instances) generated from data
530 part.model = {}
531 setmetatable(part.model, {
532__index = function (self, key)
533 return generate('model', key)
534end
535 })
536
537 -- view: drawings (AdgCanvas) availables for a single set of data
538 part.view = {}
539 setmetatable(part.view, {
540__index = function (self, key)
541 return generate('view', key)
542end
543 })
544
545 part.refresh = function (self)
546-- Regenerate all the models
547for _, model in pairs(self.model) do
548 model:reset()
549 model:regenerate(self)
550 model:changed()
551end
552
553-- Update the title block of all the views
554for _, view in pairs(self.view) do
555 local title_block = view.title_block
556 for field in pairs(Adg.TitleBlock._property) do
557local value = self.data[field:upper()]
558if value then title_block[field] = value end
559 end
560end
561 end
562
563
564 return part
565end
566
567
568return controller

Archive Download this file

Branches

Tags