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()
303 size.x = size.x + canvas:get_left_margin() + canvas:get_right_margin()
304 size.y = size.y + canvas:get_top_margin() + canvas:get_bottom_margin()
305
306 -- Create the cairo surface
307 local surface
308 if format == 'png' and cairo.ImageSurface then
309surface = cairo.ImageSurface.create(cairo.Format.RGB24, size.x, size.y)
310 elseif format == 'svg' and cairo.SvgSurface then
311surface = cairo.SvgSurface.create(file, size.x, size.y)
312 elseif format == 'pdf' and cairo.PdfSurface then
313surface = cairo.PdfSurface.create(file, size.x, size.y)
314 elseif format == 'ps' and cairo.PsSurface then
315-- Pull request: http://github.com/pavouk/lgi/pull/46
316surface = cairo.PsSurface.create(file, size.x, size.y)
317surface:dsc_comment('%%Title: adg-lua demonstration program')
318surface:dsc_comment('%%Copyright: Copyleft (C) 2013 Fontana Nicola')
319surface:dsc_comment('%%Orientation: Portrait')
320surface:dsc_begin_setup()
321surface:dsc_begin_page_setup()
322surface:dsc_comment('%%IncludeFeature: *PageSize A4')
323 elseif not format then
324format = '<nil>'
325 end
326 if not surface then
327return nil, 'Requested format not supported (' .. format .. ')'
328 end
329
330 -- Render the canvas content
331 local cr = cairo.Context.create(surface)
332 canvas:render(cr)
333 local status
334
335 if cairo.Surface.get_type(surface) == 'IMAGE' then
336status = cairo.Surface.write_to_png(surface, file)
337 else
338cr:show_page()
339status = cr.status
340 end
341
342 if status ~= 'SUCCESS' then
343return nil, cairo.Status.to_string(cairo.Status[status])
344 end
345end)
346
347local function add_title_block(canvas)
348 canvas:set_title_block(Adg.TitleBlock {
349title = '',
350author = '',
351date = '',
352drawing = '',
353logo = Adg.Logo {},
354projection = Adg.Projection { scheme = Adg.ProjectionScheme.FIRST_ANGLE },
355size = 'A4',
356 })
357end
358
359local function add_dimensions(canvas, model)
360 local body = model.body
361 local hole = model.hole
362 local dim
363
364
365 -- North
366
367 dim = Adg.LDim.new_full_from_model(body, '-D3I_X', '-D3F_X', '-D3F_Y', -math.pi/2)
368 dim:set_outside(Adg.ThreeState.OFF)
369 canvas:add(dim)
370
371 dim = Adg.LDim.new_full_from_model(body, '-D6I_X', '-D67', '-East', -math.pi/2)
372 dim:set_level(0)
373 dim:switch_extension1(false)
374 canvas:add(dim)
375
376 dim = Adg.LDim.new_full_from_model(body, '-D6I_X', '-D7F', '-East', -math.pi/2)
377 dim:set_limits('-0.06', nil)
378 canvas:add(dim)
379
380 dim = Adg.ADim.new_full_from_model(body, '-D6I_Y', '-D6F', '-D6F', '-D67', '-D6F')
381 dim:set_level(2)
382 canvas:add(dim)
383
384 dim = Adg.RDim.new_full_from_model(body, '-RD34', '-RD34_R', '-RD34_XY')
385 canvas:add(dim)
386
387 dim = Adg.LDim.new_full_from_model(body, '-DGROOVEI_X', '-DGROOVEF_X', '-DGROOVEX_POS', -math.pi/2)
388 canvas:add(dim)
389
390 dim = Adg.LDim.new_full_from_model(body, 'D2I', '-D2I', '-D2_POS', math.pi)
391 dim:set_limits('-0.1', nil)
392 dim:set_outside(Adg.ThreeState.OFF)
393 dim:set_value('\226\140\128 <>')
394 canvas:add(dim)
395
396 dim = Adg.LDim.new_full_from_model(body, 'DGROOVEI_Y', '-DGROOVEI_Y', '-DGROOVEY_POS', math.pi)
397 dim:set_limits('-0.1', nil)
398 dim:set_outside(Adg.ThreeState.OFF)
399 dim:set_value('\226\140\128 <>')
400 canvas:add(dim)
401
402
403 -- South
404
405 dim = Adg.ADim.new_full_from_model(body, 'D1F', 'D1I', 'D2I', 'D1F', 'D1F')
406 dim:set_level(2)
407 dim:switch_extension2(false)
408 canvas:add(dim)
409
410 dim = Adg.LDim.new_full_from_model(body, 'D1I', nil, 'West', math.pi / 2)
411 dim:set_ref2_from_model(hole, '-LHOLE')
412 dim:switch_extension1(false)
413 canvas:add(dim)
414
415 dim = Adg.LDim.new_full_from_model(body, 'D1I', 'DGROOVEI_X', 'West', math.pi / 2)
416 dim:switch_extension1(false)
417 dim:set_level(2)
418 canvas:add(dim)
419
420 dim = Adg.LDim.new_full_from_model(body, 'D4F', 'D6I_X', 'D4_POS', math.pi / 2)
421 dim:set_limits(nil, '+0.2')
422 dim:set_outside(Adg.ThreeState.OFF)
423 canvas:add(dim)
424
425 dim = Adg.LDim.new_full_from_model(body, 'D1F', 'D3I_X', 'D2_POS', math.pi / 2)
426 dim:set_level(2)
427 dim:switch_extension2(false)
428 dim:set_outside(Adg.ThreeState.OFF)
429 canvas:add(dim)
430
431 dim = Adg.LDim.new_full_from_model(body, 'D3I_X', 'D7F', 'East', math.pi / 2)
432 dim:set_limits(nil, '+0.1')
433 dim:set_level(2)
434 dim:set_outside(Adg.ThreeState.OFF)
435 dim:switch_extension2(false)
436 canvas:add(dim)
437
438 dim = Adg.LDim.new_full_from_model(body, 'D1I', 'D7F', 'D3F_Y', math.pi / 2)
439 dim:set_limits('-0.05', '+0.05')
440 dim:set_level(3)
441 canvas:add(dim)
442
443 dim = Adg.ADim.new_full_from_model(body, 'D4F', 'D4I', 'D5I', 'D4F', 'D4F')
444 dim:set_level(1.5)
445 dim:switch_extension2(false)
446 canvas:add(dim)
447
448
449 -- East
450
451 dim = Adg.LDim.new_full_from_model(body, 'D6F', '-D6F', 'East', 0)
452 dim:set_limits('-0.1', nil)
453 dim:set_level(4)
454 dim:set_value('\226\140\128 <>')
455 canvas:add(dim)
456
457 dim = Adg.LDim.new_full_from_model(body, 'D4F', '-D4F', 'East', 0)
458 dim:set_level(3)
459 dim:set_value('\226\140\128 <>')
460 canvas:add(dim)
461
462 dim = Adg.LDim.new_full_from_model(body, 'D5F', '-D5F', 'East', 0)
463 dim:set_limits('-0.1', nil)
464 dim:set_level(2)
465 dim:set_value('\226\140\128 <>')
466 canvas:add(dim)
467
468 dim = Adg.LDim.new_full_from_model(body, 'D7F', '-D7F', 'East', 0)
469 dim:set_value('\226\140\128 <>')
470 canvas:add(dim)
471
472
473 -- West
474
475 dim = Adg.LDim.new_full_from_model(hole, 'DHOLE', '-DHOLE', nil, math.pi)
476 dim:set_pos_from_model(body, '-West')
477 dim:set_value('\226\140\128 <>')
478 canvas:add(dim)
479
480 dim = Adg.LDim.new_full_from_model(body, 'D1I', '-D1I', '-West', math.pi)
481 dim:set_limits('-0.05', '+0.05')
482 dim:set_level(2)
483 dim:set_value('\226\140\128 <>')
484 canvas:add(dim)
485
486 dim = Adg.LDim.new_full_from_model(body, 'D3I_Y', '-D3I_Y', '-West', math.pi)
487 dim:set_limits('-0.25', nil)
488 dim:set_level(3)
489 dim:set_value('\226\140\128 <>')
490 canvas:add(dim)
491end
492
493function generator.view.detailed(part)
494 local canvas = Adg.Canvas {}
495 local model = part.model
496
497 add_title_block(canvas)
498 canvas:add(Adg.Stroke { trail = model.body })
499 canvas:add(Adg.Stroke { trail = model.edges })
500 canvas:add(Adg.Hatch { trail = model.hole })
501 canvas:add(Adg.Stroke { trail = model.hole })
502 canvas:add(Adg.Stroke {
503trail = model.axis,
504line_dress = Adg.Dress.LINE_AXIS
505 })
506 add_dimensions(canvas, model)
507
508 return canvas
509end
510
511
512-- CONTROLLER
513-----------------------------------------------------------------
514
515local controller = {}
516
517function controller.new(data)
518 local part = {}
519
520 local function generate(class, method)
521local constructor = generator[class][method]
522local result = constructor and constructor(part) or false
523part[class][method] = result
524return result
525 end
526
527 -- data: numbers and strings needed to define the whole part
528 part.data = data or {}
529
530 -- model: different models (AdgModel instances) generated from data
531 part.model = {}
532 setmetatable(part.model, {
533__index = function (self, key)
534 return generate('model', key)
535end
536 })
537
538 -- view: drawings (AdgCanvas) availables for a single set of data
539 part.view = {}
540 setmetatable(part.view, {
541__index = function (self, key)
542 return generate('view', key)
543end
544 })
545
546 return part
547end
548
549
550return controller

Archive Download this file

Branches

Tags