Class: Topolys::Model

Inherits:
Object
  • Object
show all
Defined in:
lib/topolys/model.rb

Overview

The Topolys Model contains many Topolys Objects, a Topolys Object can only be connected to other Topolys Objects in the same Topolys Model. To enforce this Topolys Objects should not be constructed directly, they should be retrieved using the Topolys Model get_* object methods.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tol = nil) ⇒ Model

Returns a new instance of Model.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/topolys/model.rb', line 70

def initialize(tol=nil)

  # changing tolerance on a model after construction would be very complicated
  # you would have to go through and regroup points, etc
  if !tol.is_a?(Numeric)
    tol = 0.01
  end
  @tol = tol
  @tol2 = @tol**2

  @vertices = []
  @edges = []
  @directed_edges = []
  @wires = []
  @faces = []
  @shells = []
  @cells = []
end

Instance Attribute Details

#cellsObject (readonly)

Returns the value of attribute cells.



67
68
69
# File 'lib/topolys/model.rb', line 67

def cells
  @cells
end

#directed_edgesObject (readonly)

Returns the value of attribute directed_edges.



67
68
69
# File 'lib/topolys/model.rb', line 67

def directed_edges
  @directed_edges
end

#edgesObject (readonly)

Returns the value of attribute edges.



67
68
69
# File 'lib/topolys/model.rb', line 67

def edges
  @edges
end

#facesObject (readonly)

Returns the value of attribute faces.



67
68
69
# File 'lib/topolys/model.rb', line 67

def faces
  @faces
end

#shellsObject (readonly)

Returns the value of attribute shells.



67
68
69
# File 'lib/topolys/model.rb', line 67

def shells
  @shells
end

#tolObject (readonly)

Returns the value of attribute tol.



68
69
70
# File 'lib/topolys/model.rb', line 68

def tol
  @tol
end

#tol2Object (readonly)

Returns the value of attribute tol2.



68
69
70
# File 'lib/topolys/model.rb', line 68

def tol2
  @tol2
end

#verticesObject (readonly)

Returns the value of attribute vertices.



67
68
69
# File 'lib/topolys/model.rb', line 67

def vertices
  @vertices
end

#wiresObject (readonly)

Returns the value of attribute wires.



67
68
69
# File 'lib/topolys/model.rb', line 67

def wires
  @wires
end

Class Method Details

.from_json(obj) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/topolys/model.rb', line 106

def self.from_json(obj)
  model = Model.new
  id_map = {}

  obj[:vertices].each do |v|
    p = v[:point]
    point = Point3D.new(p[:x], p[:y], p[:z])
    vertex = model.get_vertex(point)
    set_id(vertex, v[:id])
    vertex.attributes = v[:attributes] if v[:attributes]
    id_map[v[:id]] = vertex
  end

  obj[:edges].each do |e|
    v0 = id_map[e[:v0]]
    v1 = id_map[e[:v1]]
    edge = model.get_edge(v0, v1)
    set_id(edge, e[:id])
    edge.attributes = e[:attributes] if e[:attributes]
    id_map[e[:id]] = edge
  end

  obj[:directed_edges].each do |de|
    edge = id_map[de[:edge]]
    inverted = de[:inverted]
    directed_edge = nil
    if inverted
      directed_edge = model.get_directed_edge(edge.v1, edge.v0)
    else
      directed_edge = model.get_directed_edge(edge.v0, edge.v1)
    end
    set_id(directed_edge, de[:id])
    directed_edge.attributes = de[:attributes] if de[:attributes]
    id_map[de[:id]] = directed_edge
  end

  obj[:wires].each do |w|
    vertices = []
    w[:directed_edges].each do |id|
      directed_edge = id_map[id]
      vertices << directed_edge.v0
    end
    wire = model.get_wire(vertices)
    set_id(wire, w[:id])
    wire.attributes = w[:attributes] if w[:attributes]
    id_map[w[:id]] = wire
  end

  obj[:faces].each do |f|
    outer = id_map[f[:outer]]
    holes = []
    f[:holes].each do |id|
      holes << id_map[id]
    end
    face = model.get_face(outer, holes)
    set_id(face, f[:id])
    face.attributes = f[:attributes] if f[:attributes]
    id_map[f[:id]] = face
  end

  obj[:shells].each do |s|
    faces = []
    s[:faces].each do |id|
      faces << id_map[id]
    end
    shell = model.get_shell(faces)
    set_id(shell, s[:id])
    shell.attributes = s[:attributes] if s[:attributes]
    id_map[s[:id]] = shell
  end

  return model
end

.schemaObject



184
185
186
187
# File 'lib/topolys/model.rb', line 184

def self.schema
  s = File.read(schema_file)
  return JSON.parse(s)
end

.schema_fileObject



180
181
182
# File 'lib/topolys/model.rb', line 180

def self.schema_file
  return File.join(File.dirname(__FILE__), "./schema/topolys.json")
end

Instance Method Details

#all_objectsObject



89
90
91
# File 'lib/topolys/model.rb', line 89

def all_objects
  @vertices + @edges + @directed_edges + @wires + @faces + @shells + @cells
end

#find_existing_directed_edge(v0, v1) ⇒ DirectedEdge

Returns DirectedEdge.

Parameters:

Returns:



349
350
351
352
353
354
355
356
357
# File 'lib/topolys/model.rb', line 349

def find_existing_directed_edge(v0, v1)
  # search for directed edge and return if it exists
  @directed_edges.each do |de|
    if (de.v0.id == v0.id) && (de.v1.id == v1.id)
      return de
    end
  end
  return nil
end

#find_existing_edge(v0, v1) ⇒ Edge

Returns Edge or nil.

Parameters:

Returns:

  • (Edge)

    Edge or nil



314
315
316
317
318
319
320
321
322
323
# File 'lib/topolys/model.rb', line 314

def find_existing_edge(v0, v1)
  @edges.each do |e|
    if (e.v0.id == v0.id) && (e.v1.id == v1.id)
      return e
    elsif (e.v0.id == v1.id) && (e.v1.id == v0.id)
      return e
    end
  end
  return nil
end

#find_existing_vertex(point) ⇒ Vertex

Returns Vertex or nil.

Parameters:

Returns:



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/topolys/model.rb', line 266

def find_existing_vertex(point)
  # search for point and return corresponding vertex if it exists
  # otherwise create new vertex
  @vertices.each do |v|
    p = v.point

    ## L1 norm
    #if ((p.x-point.x).abs < @tol) &&
    #    (p.y-point.y).abs < @tol) &&
    #    (p.z-point.z).abs < @tol))
    #  return v
    #end

    # L2 norm
    if ((p.x-point.x)**2 + (p.y-point.y)**2 + (p.z-point.z)**2) < @tol2
      return v
    end
  end

  return nil
end

#get_directed_edge(v0, v1) ⇒ DirectedEdge

Returns DirectedEdge.

Parameters:

Returns:



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/topolys/model.rb', line 328

def get_directed_edge(v0, v1)
  # search for directed edge and return if it exists
  de = find_existing_directed_edge(v0, v1)
  return de if de

  # otherwise create new directed edge
  edge = get_edge(v0, v1)

  inverted = false
  if (edge.v0.id != v0.id)
    inverted = true
  end

  directed_edge = DirectedEdge.new(edge, inverted)
  @directed_edges << directed_edge
  return directed_edge
end

#get_edge(v0, v1) ⇒ Edge

Returns Edge.

Parameters:

Returns:



297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/topolys/model.rb', line 297

def get_edge(v0, v1)
  # search for edge and return if it exists
  e = find_existing_edge(v0, v1)
  return e if e

  # otherwise create new edge
  @vertices << v0 if !@vertices.find {|v| v.id == v0.id}
  @vertices << v1 if !@vertices.find {|v| v.id == v1.id}

  edge = Edge.new(v0, v1)
  @edges << edge
  return edge
end

#get_face(outer, holes) ⇒ Face

Returns Face Returns Face or nil if wires are not in model.

Parameters:

  • outer (Wire)

    Outer wire

  • holes (Array)

    Array of Wire

Returns:

  • (Face)

    Face Returns Face or nil if wires are not in model



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/topolys/model.rb', line 395

def get_face(outer, holes)
  # search for face and return if exists
  # otherwise create new face

  hole_ids = holes.map{|h| h.id}.sort
  @faces.each do |face|
    if face.outer.id == outer.id
      if face.holes.map{|h| h.id}.sort == hole_ids
        return face
      end
    end
  end

  # all the wires have to be in the model
  return nil if @wires.index{|w| w.id == outer.id}.nil?
  holes.each do |hole|
    return nil if @wires.index{|w| w.id == outer.id}.nil?
  end

  face = nil
  begin
    face = Face.new(outer, holes)
    @faces << face
  rescue => exception
    puts exception
  end

  return face
end

#get_reverse(object) ⇒ Object

Returns reversed object

Parameters:

Returns:

  • (Object)

    Returns reversed object



456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/topolys/model.rb', line 456

def get_reverse(object)
  if object.is_a?(Vertex)
    return object
  elsif object.is_a?(Edge)
    return object
  elsif object.is_a?(DirectedEdge)
    return get_directed_edge(object.v1, object.v0)
  elsif object.is_a?(Wire)
    return get_wire(object.vertices.reverse)
  elsif object.is_a?(Face)
    reverse_outer = get_wire(object.outer.vertices.reverse)
    reverse_holes = []
    object.holes.each do |hole|
      reverse_holes << get_wire(hole.vertices.reverse)
    end
    return get_face(reverse_outer, reverse_holes)
  elsif object.is_a?(Shell)
    # can't reverse a shell
    return nil
  end

  return nil
end

#get_shell(faces) ⇒ Shell

Returns Shell or nil if faces are not in model or not connected

Parameters:

  • faces (Array)

    Array of Face

Returns:

  • (Shell)

    Returns Shell or nil if faces are not in model or not connected



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/topolys/model.rb', line 427

def get_shell(faces)

  # all the faces have to be in the model
  faces.each do |face|
    return nil if @faces.index{|f| f.id == face.id}.nil?
  end

  # check if we already have this shell
  face_ids = faces.map{|f| f.id}.sort
  @shells.each do |shell|
    if shell.faces.map{|f| f.id}.sort == face_ids
      return shell
    end
  end

  # create a new shell
  shell = nil
  begin
    shell = Shell.new(faces)
    @shells << shell
  rescue => exception
    puts exception
  end

  return shell
end

#get_vertex(point) ⇒ Vertex

Returns Vertex.

Parameters:

Returns:



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/topolys/model.rb', line 233

def get_vertex(point)
  # search for point and return corresponding vertex if it exists
  v = find_existing_vertex(point)
  return v if v

  # otherwise create new vertex
  v = Vertex.new(point)
  @vertices << v

  # check if this vertex needs to be inserted on any edge
  updated = false
  @edges.each do |edge|
    if new_point = vertex_intersect_edge(v, edge)

      # point might need to be added to multiple edges
      # point can be updated to project it onto edge, don't update multiple times
      if !updated
        # simulate friend access to set point on vertex
        v.instance_variable_set(:@point, new_point)
        v.recalculate
        updated = true
      end

      # now split the edge with this vertex
      split_edge(edge, v)
    end
  end

  return v
end

#get_vertices(points) ⇒ Array

Returns Array of Vertex.

Parameters:

  • points (Array)

    Array of Point3D

Returns:

  • (Array)

    Array of Vertex



290
291
292
# File 'lib/topolys/model.rb', line 290

def get_vertices(points)
  points.map {|p| get_vertex(p)}
end

#get_wire(vertices) ⇒ Wire

Returns Wire.

Parameters:

  • vertices (Array)

    Array of Vertex, assumes closed wire (e.g. first vertex is also last vertex)

Returns:



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/topolys/model.rb', line 361

def get_wire(vertices)
  # search for wire and return if exists
  # otherwise create new wire

  # insert any existing model vertices that should be inserted on the edges in vertices
  vertices = insert_vertices_on_edges(vertices)

  n = vertices.size
  directed_edges = []
  vertices.each_index do |i|
    directed_edges << get_directed_edge(vertices[i], vertices[(i+1)%n])
  end

  # see if we already have this wire
  @wires.each do |wire|
    if wire.circular_equal?(directed_edges)
      return wire
    end
  end

  wire = nil
  begin
    wire = Wire.new(directed_edges)
    @wires << wire
  rescue => exception
    puts exception
  end

  return wire
end

#save(file) ⇒ Object



193
194
195
196
197
# File 'lib/topolys/model.rb', line 193

def save(file)
  File.open(file, 'w') do |file|
    file.puts self.to_s
  end
end

#save_graphviz(file) ⇒ Object



211
212
213
214
215
# File 'lib/topolys/model.rb', line 211

def save_graphviz(file)
  File.open(file, 'w') do |file|
    file.puts to_graphviz
  end
end

#to_graphvizObject



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/topolys/model.rb', line 199

def to_graphviz
  result = "digraph model {\n"
  result += "  rankdir=LR\n"
  all_objects.each do |obj|
    obj.children.each { |child| result += "  #{child.short_name} -> #{obj.short_name}\n" }
    #obj.parents.each { |parent| result += "  #{parent.short_name} -> #{obj.short_name}\n" }
  end
  result += " }"

  return result
end

#to_jsonObject



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/topolys/model.rb', line 93

def to_json
  result= {
    vertices: @vertices.map { |v| v.to_json },
    edges: @edges.map { |e| e.to_json },
    directed_edges: @directed_edges.map { |de| de.to_json },
    wires: @wires.map { |w| w.to_json },
    faces: @faces.map { |f| f.to_json },
    shells: @shells.map { |s| s.to_json },
    cells: @cells.map { |c| c.to_json }
  }
  return result
end

#to_sObject



189
190
191
# File 'lib/topolys/model.rb', line 189

def to_s
  JSON.pretty_generate(to_json)
end

#vertex_intersect_edge(vertex, edge) ⇒ Point3D

Returns Point3D of vertex projected on edge or nil.

Parameters:

Returns:

  • (Point3D)

    Point3D of vertex projected on edge or nil



220
221
222
223
224
225
226
227
228
229
# File 'lib/topolys/model.rb', line 220

def vertex_intersect_edge(vertex, edge)
  if vertex.id == edge.v0.id || vertex.id == edge.v1.id
    return nil
  end

  # new_point, length - length unused here
  new_point, _ = project_point_on_edge(edge.v0.point, edge.v1.point, vertex.point)

  return new_point
end