[[PageOutline]]
PythonOCC omogoča enostavnejšo uporabo jedra modelirnika OpenCascade v jeziku Python.
Prednost Pythona v primerjavi z C++ je:
- Prenosljivosti. Programi se interpretirajo in jih ni potrebno prevajati zato delujejo na vseh operacijskih sistemih. So pa nekoliko počasnejši.
- Enostavnejša namestitev potrebnih knjižnic, brez zahtevne konfiguracije povezovalnih parametrov, ki so značilni za C++.
- Lažje učenje jezika. V interaktivnem načinu obstaja tudi refleksija oziroma dinamično prepoznavanje možnih ukazov v objektu.
Za vaje, si je potrebno na osebnem računalniku z 64-bitnimi Windows-i pripraviti okolje po naslednjih korakih:
1. [https://repo.continuum.io/archive/Anaconda3-4.2.0-Windows-x86_64.exe Anaconda] upravljalnik paketov.
2. V terminalu napišite ukaz: {{{conda install -c https://conda.anaconda.org/dlr-sc pythonocc-core=0.17}}}
3. [https://www.jetbrains.com/pycharm/download/download_thanks.jsp?edition=comm&os=win PyCharm] urejevalnik.
6. Ustvarite datoteko {{{core-hello.py}}} z naslednjo vsebino in preverite ali vam prikaže kocko v 3D.
{{{
#!python
from OCC.Display.SimpleGui import init_display
from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox
display, start_display, add_menu, add_function_to_menu = init_display()
my_box = BRepPrimAPI_MakeBox(10., 20., 30.).Shape()
display.DisplayShape(my_box, update=True)
start_display()
}}}
{{{
#!comment
V primeru, da nam namestitveni program nepravilno namesti imenik na {{{C:\OCC}}} ga prestavimo v podimenik {{{C:\Python34\Lib\site-packages}}}
Neobvezno, vendar priporočljivo je namestiti še:
1. '''iPython''' za interektivno delo
2. '''NumPy''' za delo z numeričnimi metodami in matrikami
3. '''SciPy''' za delo z znanstvenimi algoritmi
Vse tri naštete pakete lahko namestite direktno z namestitvijo Python distribucije [https://3230d63b5fc54e62148e-c95ac804525aac4b6dba79b00b39d1d3.ssl.cf1.rackcdn.com/Anaconda3-2.3.0-Windows-x86_64.exe Anaconda].
}}}
== Predstavitev CAD-jedra Open CASCADE na primerih ==
Osnove gradnje modelov so podane na strani [wiki:PythonOcc/primitives] in [wiki:PythonOcc/elbow]
=== Uvod v modeliranje PythonOCC s 3D primitivi ===
V tem primeru je namen pokazati enostavne aplikacije:
* izdelava menuja in dva podmenuja
* ta dva menuja izdelata: i. kocko in ii. cilinder
Najprej kličemo knjižnico za enostaven uporabniški vmesnik SimpleGUI:
{{{
#!python
# -*- coding: utf-8 -*-
from OCC.Display.SimpleGui import *
}}}
V naslednjem koraku inicializiramo funkcije za display:
{{{
#!python
display, start_display, add_menu, add_function_to_menu = init_display()
}}}
[[Image(3Dprimitives.png, width=480px, right)]]
Nato definiramo dve funkciji, ki izdelata kocko in cilinder:
{{{
#!python
def kocka(event=None):
from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox
display.EraseAll()
my_box= BRepPrimAPI_MakeBox(10., 20., 30.)
display.DisplayShape(my_box.Shape())
def valj(event=None):
from OCC.BRepPrimAPI import BRepPrimAPI_MakeCylinder
display.EraseAll()
my_cylinder = BRepPrimAPI_MakeCylinder(60, 200)
display.DisplayShape(my_cylinder.Shape())
}}}
Izdelava menija 'simple test' in dodajanje funkcij v podmenije:
{{{
#!python
add_menu('enostaven primer')
add_function_to_menu('enostaven primer', kocka)
add_function_to_menu('enostaven primer', valj)
}}}
Na koncu renderiramo 3D model:
{{{
#!python
display.View_Iso()
display.FitAll()
start_display() # Zanka start_display() je neskončna
}}}
Namesto uporabe klasičnih primitivov, lahko kocko zgradimo tudi z izvlekom žičnega modela.
{{{
#!python
# -*- coding: utf-8 -*-
## Izdelava kocke
from OCC.Display.SimpleGui import *
from OCC.BRepPrimAPI import *
from OCC.gp import *
from OCC.GC import *
from OCC.BRepBuilderAPI import *
#from OCC.TopoDS import *
display, start_display, add_menu, add_function_to_menu = init_display()
#Definiranje točk v prostoru
aPnt1 = gp_Pnt(0 , 0 , 0)
aPnt2 = gp_Pnt(10 , 0, 0)
aPnt3 = gp_Pnt(10 , 10 , 0)
aPnt4 = gp_Pnt(0, 10 , 0)
#Izdelava segmentov--definiranje geometrije
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt2 , aPnt3)
aSegment3 = GC_MakeSegment(aPnt3 , aPnt4)
aSegment4 = GC_MakeSegment(aPnt4 , aPnt1)
#Izdelava robov -- definiranje topologije
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment3.Value())
aEdge4 = BRepBuilderAPI_MakeEdge(aSegment4.Value())
'''
#Robove se lahko določi tudi direktno s točkami:
aEdge1 = BRepBuilderAPI_MakeEdge(aPnt1, aPnt2)
aEdge2 = BRepBuilderAPI_MakeEdge(aPnt2, aPnt3)
aEdge3 = BRepBuilderAPI_MakeEdge(aPnt3, aPnt4)
aEdge4 = BRepBuilderAPI_MakeEdge(aPnt4, aPnt1)
'''
#Povezovanje robov v mrežo
aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,\
aEdge3.Edge(), aEdge4.Edge())
#Telo: Iz profila se izdela telo
myFaceProfile = BRepBuilderAPI_MakeFace(aWire.Wire())
aPrismVec = gp_Vec(0 , 0 , 10)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face() , aPrismVec).Shape()
#Spodaj so zakomentirani ukazi za prikazovanje posameznih delov.
'''
#Prikaz točk
display.DisplayShape(aPnt1)
display.DisplayShape(aPnt2)
display.DisplayShape(aPnt3)
display.DisplayShape(aPnt4)
'''
'''
#Prikaz robov
display.DisplayShape(aEdge1.Shape())
display.DisplayShape(aEdge2.Shape())
display.DisplayShape(aEdge3.Shape())
display.DisplayShape(aEdge4.Shape())
'''
'''
#Prikaz mreže
display.DisplayShape(aWire.Shape())
'''
'''
#Prikaz profila
display.DisplayShape(myFaceProfile.Shape())
'''
#Prikaz telesa
display.DisplayShape(myBody)
start_display()
}}}
=== Slikovni pregled izdelave 3D modela ===
a. Definiranje točk v prostoru:
{{{
#!python
aPnt1 = gp_Pnt(x_1 , y_1 , z_1) --> aPnt4 = gp_Pnt(x_4 , y_4 , z_4)
}}}
b. Iz točk v prostoru se tvori robove (en rob je sestavljen iz najmanj dveh točk):
{{{
#!python
myEdge1 = BRepBuilderAPI_MakeEdge(aPnt1, aPnt2) --> myEdge4 = BRepBuilderAPI_MakeEdge(aPnt4, aPnt1)
}}}
c. Ko imamo vse robove izdelamo mrežo:
{{{
#!python
myWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,aEdge3.Edge(), aEdge4.Edge())
}}}
d. Iz mreže tvorimo površino (mreža mora biti zaprta):
{{{
#!python
myFace = BRepBuilderAPI_MakeFace(myWire)
}}}
e. Definiranje prostorskega vektorja, ki bo določal smer izvleka (ekstrudiranja) površine:
{{{
#!python
myVec = gp_Vec(n_x , n_y , n_z)
}}}
f. Z površino in smernim vektorjem izdelamo 3D model:
{{{
#!python
myBody = BRepPrimAPI_MakePrism(myFace.Face() , myVec)
}}}
[[Image(points-solid.svg)]]
=== Pregled uporabljenih OCC knjižnic ===
{{{
#!python
## Importanje različnih knjižnic
# Uporabniški vmesnik GUI
from OCC.Display.SimpleGui import *
# Matematična knjižnica
import math
# OpenCascade
from OCC.gp import * #točke
from OCC.BRepBuilderAPI import * #gradimo robove, segmente, mreže ...
from OCC.BRepPrimAPI import * #izdelava osnovnih geometrijskih primitivov
from OCC.BRepFilletAPI import * #izdelava zaokrožitev
}}}
Točnejša navodila, opis funkcij in knjižnic lahko dobimo v PythonOCC dokumentaciji.
=== Inicializacija zaslona in izdelava grafičnega vmesnika===
{{{
#!python
# OCC.Display.SimpleGui.init_display() returns multiple
# values which are assigned here
display, start_display, add_menu, add_function_to_menu = \
init_display()
draw_bottle() #kličemo CAD model, ki ga želimo prikazati na zaslonu
start_display()
}}}
=== Risanje točk v prostoru ===
Izdelava točk v prostoru je najosnovnejša operacija v OCC.
{{{
#!python
# Definiranje začetnih točk
aPnt1 = gp_Pnt(-myWidth / 2. , 0 , 0)
aPnt2 = gp_Pnt(-myWidth / 2. , -myThickness / 4. , 0)
aPnt3 = gp_Pnt(0 , -myThickness / 2. , 0)
aPnt4 = gp_Pnt(myWidth / 2. , -myThickness / 4. , 0)
aPnt5 = gp_Pnt(myWidth / 2. , 0 , 0)
}}}
=== Izdelava robnih elementov ===
V naslednjem koraku se iz začetnih točk izdela robove:
{{{
#!python
# Definiranje geometrije
aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4)
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt4 , aPnt5)
# Definiranje topologije
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
}}}
=== Povezovanje robnih elementov v mreže ===
Robne elemente se v nadaljevanju združi v mrežo.
{{{
#!python
# Izdelava mreže
aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,aEdge3.Edge())
}}}
=== Uporaba funkcij za izdelavo objektov v prostoru ===
{{{
#!python
# Izdelava celotnega profila - mirror
xAxis = gp_OX()
aTrsf = gp_Trsf()
aTrsf.SetMirror(xAxis)
aBRepTrsf = BRepBuilderAPI_Transform(aWire.Shape() , aTrsf)
aMirroredShape = aBRepTrsf.Shape()
aMirroredWire = topods_Wire(aMirroredShape)
mkWire = BRepBuilderAPI_MakeWire()
mkWire.Add(aWire.Wire())
mkWire.Add(aMirroredWire)
myWireProfile = mkWire.Wire()
# Telo: Iz profila se izdela telo (Funkcija izvleka 3D)
myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile)
aPrismVec = gp_Vec(0 , 0 , myHeight)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face() , aPrismVec)
}}}
=== Risanje geometrijskih primitivov ===
V OCC že obstajajo funkcije za izdelavo geometrijskih primitivov (kocka, valj,...), kar je prikazano na spodnjem primeru.
{{{
#!python
from OCC.Display.SimpleGui import *
from OCC.BRepPrimAPI import *
display, start_display, add_menu, add_function_to_menu = init_display()
my_box = BRepPrimAPI_MakeBox(10.,20.,30.).Shape()
# ali my_cylinder = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius , myNeckHeight), kjer so spremenljivke že preddefinirane
display.DisplayShape(my_box) # ali display.DisplayShape(my_cylinder)
start_display()
}}}
=== Risanje različnih tipov robov ===
[[Image(edge_primer.PNG, width=480px, right)]]
{{{
##Copyright 2009-2015 Thomas Paviot (tpaviot@gmail.com)
##
##This file is part of pythonOCC.
##
##pythonOCC is free software: you can redistribute it and/or modify
##it under the terms of the GNU Lesser General Public License as published by
##the Free Software Foundation, either version 3 of the License, or
##(at your option) any later version.
##
##pythonOCC is distributed in the hope that it will be useful,
##but WITHOUT ANY WARRANTY; without even the implied warranty of
##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
##GNU Lesser General Public License for more details.
##
##You should have received a copy of the GNU Lesser General Public License
##along with pythonOCC. If not, see .
import math
from OCC.gp import gp_Pnt, gp_Lin, gp_Ax1, gp_Dir, gp_Elips, gp_Ax2
from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge,
BRepBuilderAPI_MakeVertex)
from OCC.TColgp import TColgp_Array1OfPnt
from OCC.Geom import Geom_BezierCurve
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()
def edge(event=None):
# The blue edge
BlueEdge = BRepBuilderAPI_MakeEdge(gp_Pnt(-80, -50, -20),
gp_Pnt(-30, -60, -60))
V1 = BRepBuilderAPI_MakeVertex(gp_Pnt(-20, 10, -30))
V2 = BRepBuilderAPI_MakeVertex(gp_Pnt(10, 7, -25))
YellowEdge = BRepBuilderAPI_MakeEdge(V1.Vertex(), V2.Vertex())
#The white edge
line = gp_Lin(gp_Ax1(gp_Pnt(10, 10, 10), gp_Dir(1, 0, 0)))
WhiteEdge = BRepBuilderAPI_MakeEdge(line, -20, 10)
#The red edge
Elips = gp_Elips(gp_Ax2(gp_Pnt(10, 0, 0), gp_Dir(1, 1, 1)), 60, 30)
RedEdge = BRepBuilderAPI_MakeEdge(Elips, 0, math.pi/2)
# The green edge and the both extreme vertex
P1 = gp_Pnt(-15, 200, 10)
P2 = gp_Pnt(5, 204, 0)
P3 = gp_Pnt(15, 200, 0)
P4 = gp_Pnt(-15, 20, 15)
P5 = gp_Pnt(-5, 20, 0)
P6 = gp_Pnt(15, 20, 0)
P7 = gp_Pnt(24, 120, 0)
P8 = gp_Pnt(-24, 120, 12.5)
array = TColgp_Array1OfPnt(1, 8)
array.SetValue(1, P1)
array.SetValue(2, P2)
array.SetValue(3, P3)
array.SetValue(4, P4)
array.SetValue(5, P5)
array.SetValue(6, P6)
array.SetValue(7, P7)
array.SetValue(8, P8)
curve = Geom_BezierCurve(array)
ME = BRepBuilderAPI_MakeEdge(curve.GetHandle())
GreenEdge = ME
V3 = ME.Vertex1()
V4 = ME.Vertex2()
display.DisplayColoredShape(BlueEdge.Edge(), 'BLUE')
display.DisplayShape(V1.Vertex())
display.DisplayShape(V2.Vertex())
display.DisplayColoredShape(WhiteEdge.Edge(), 'WHITE')
display.DisplayColoredShape(YellowEdge.Edge(), 'YELLOW')
display.DisplayColoredShape(RedEdge.Edge(), 'RED')
display.DisplayColoredShape(GreenEdge.Edge(), 'GREEN')
display.DisplayShape(V3)
display.DisplayShape(V4, update=True)
if __name__ == '__main__':
edge()
start_display()
}}}
=== Risanje prerezane piramide ===
[[Image(prerezana_piramida.PNG, width=480px, right)]]
{{{
##Copyright 2009-2015 Thomas Paviot (tpaviot@gmail.com)
##
##This file is part of pythonOCC.
##
##pythonOCC is free software: you can redistribute it and/or modify
##it under the terms of the GNU Lesser General Public License as published by
##the Free Software Foundation, either version 3 of the License, or
##(at your option) any later version.
##
##pythonOCC is distributed in the hope that it will be useful,
##but WITHOUT ANY WARRANTY; without even the implied warranty of
##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
##GNU Lesser General Public License for more details.
##
##You should have received a copy of the GNU Lesser General Public License
##along with pythonOCC. If not, see .
import math
from OCC.gp import gp_Dir, gp_Pln, gp_Ax3, gp_XOY
from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.BRepOffsetAPI import BRepOffsetAPI_DraftAngle
from OCC.Precision import precision_Angular
from OCC.BRep import BRep_Tool_Surface
from OCC.TopExp import TopExp_Explorer
from OCC.TopAbs import TopAbs_FACE
from OCC.Geom import Handle_Geom_Plane_DownCast
from OCC.TopoDS import topods_Face
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()
def draft_angle(event=None):
S = BRepPrimAPI_MakeBox(200., 300., 150.).Shape()
adraft = BRepOffsetAPI_DraftAngle(S)
topExp = TopExp_Explorer()
topExp.Init(S, TopAbs_FACE)
while topExp.More():
face = topods_Face(topExp.Current())
surf = Handle_Geom_Plane_DownCast(BRep_Tool_Surface(face)).GetObject()
dirf = surf.Pln().Axis().Direction()
ddd = gp_Dir(0, 0, 1)
if dirf.IsNormal(ddd, precision_Angular()):
adraft.Add(face, ddd, math.radians(15), gp_Pln(gp_Ax3(gp_XOY())))
topExp.Next()
adraft.Build()
display.DisplayShape(adraft.Shape(), update=True)
if __name__ == '__main__':
draft_angle()
start_display()
}}}
=== Izdelava primera Bottle z uporabo programskega jezika Python in knjižnice OCC ===
Naslednji primer prikazuje izdelavo primera BottleCAD. Podrobnejši razdelek posameznih delov programske kode dobimo na [[http://trac.lecad.si/vaje/wiki/OpenCascade|MakeBottleCAD(C++)]].
[[Image(bottle.png, width=480px, right)]]
{{{
#!python
##Copyright 2011 Simon Kulovec (simon.kulovec@lecad.si)
##Uredil in posodobil 2015 Dejan Penko (dejan.penko@lecad.fs.uni-lj.si)
##Example: MakeCADBottle
##This file is part of pythonOCC.
## Importanje različnih knjižnic
# Uporabniški vmesnik GUI
from OCC.Display.SimpleGui import *
# OpenCascade
from OCC.gp import *
from OCC.TopoDS import *
from OCC.GC import *
from OCC.BRepBuilderAPI import *
from OCC.BRepPrimAPI import *
from OCC.BRepFilletAPI import *
from OCC.BRepAlgoAPI import *
from OCC.Geom import *
from OCC.Geom2d import *
from OCC.GCE2d import *
from OCC.BRep import *
from OCC.BRepLib import *
from OCC.BRepOffsetAPI import *
from OCC.TopTools import *
from OCC.TopAbs import *
from OCC.TopExp import *
import math
#Naslednja podprograma "face_is_plane" in "geom_plane_from_face" sta napisana za pomoč v nadaljevanju programa
def face_is_plane(face):
"""
Vrne True če je TopoDS_Shape ravnina, False v nasprotnem primeru
"""
hs = BRep_Tool_Surface(face)
downcast_result = Handle_Geom_Plane.DownCast(hs)
# Vrednost je "null", če se izvedba tega podprograma ne izvede pravilo ali je ni mogoče izvesti. Iz tega sledi, da profil ni ravnina
if downcast_result.IsNull():
return False
else:
return True
def geom_plane_from_face(aFace):
"""
Vrne subjekt geometrične ravnine iz ravninske površine
"""
return Handle_Geom_Plane.DownCast(BRep_Tool_Surface(aFace)).GetObject()
def show_bottle(aRes):
display.EraseAll()
#print (dir(display))
display.DisplayShape(aRes)
def define_body(myWidth, myThickness, myHeight):
## Definiranje začetnih točk
aPnt1 = gp_Pnt(-myWidth / 2. , 0 , 0)
aPnt2 = gp_Pnt(-myWidth / 2. , -myThickness / 4. , 0)
aPnt3 = gp_Pnt(0 , -myThickness / 2. , 0)
aPnt4 = gp_Pnt(myWidth / 2. , -myThickness / 4. , 0)
aPnt5 = gp_Pnt(myWidth / 2. , 0 , 0)
## Definiranje geometrije
aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4)
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt4 , aPnt5)
## Definiranje topologije
#Definiranje robov
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
#Definiranje robov iz mreže
aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,aEdge3.Edge())
## Izdelava celotnega profila - mirror
#Definiramo os zrcaljenja (os X)
xAxis = gp_OX()
#Nastavimo "zrcalo"
aTrsf = gp_Trsf()
aTrsf.SetMirror(xAxis)
#Uporabimo zrcalno transformacijo
aBRepTrsf = BRepBuilderAPI_Transform(aWire.Shape() , aTrsf)
#Zrcalno obliko dobimo nazaj iz transformacije in pretvorimo v mrežo
aMirroredShape = aBRepTrsf.Shape()
aMirroredWire = topods_Wire(aMirroredShape)
#Kombiniramo dve konsistentni mreži
mkWire = BRepBuilderAPI_MakeWire()
mkWire.Add(aWire.Wire())
mkWire.Add(aMirroredWire)
myWireProfile = mkWire.Wire()
## Telo:
#Iz profila se izdela telo
myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile)
aPrismVec = gp_Vec(0 , 0 , myHeight)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face() , aPrismVec)
#Dodamo zaokrožitve (fillet) s pomočjo t. i. Explorerja
mkFillet = BRepFilletAPI_MakeFillet(myBody.Shape())
anEdgeExplorer = TopExp_Explorer(myBody.Shape(), TopAbs_EDGE)
while anEdgeExplorer.More():
anEdge = topods.Edge(anEdgeExplorer.Current())
mkFillet.Add(myThickness / 12.0, anEdge)
anEdgeExplorer.Next()
myBody = mkFillet
##Dodajanje grla na steklenico
neckLocation = gp_Pnt(0, 0, myHeight)
neckNormal = gp_DZ()
neckAx2 = gp_Ax2(neckLocation, neckNormal)
myNeckRadius = myThickness / 4
myNeckHeight = myHeight / 10
mkCylinder = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius , myNeckHeight)
myNeck = mkCylinder;
myBody = BRepAlgoAPI_Fuse(myBody.Shape(), myNeck.Shape())
## Izdelava votle steklenice
#CILJ: Poiščemo najvišji Z profil in ga odstranimo
faceToRemove = None
zMax = -1;
#Če želimo najti najvišji Z profil, katerega želimo odstraniti iz lupine, je potrebno iti skozi vse profile
aFaceExplorer = TopExp_Explorer(myBody.Shape(), TopAbs_FACE)
while aFaceExplorer.More():
aFace = topods.Face(aFaceExplorer.Current())
if face_is_plane(aFace):
aPlane = geom_plane_from_face(aFace)
# We want the highest Z face, so compare this to the previous faces
aPnt = aPlane.Location()
aZ = aPnt.Z()
if aZ > zMax:
zMax = aZ
faceToRemove = aFace
aFaceExplorer.Next()
facesToRemove = TopTools_ListOfShape()
facesToRemove.Append(faceToRemove)
myBody = BRepOffsetAPI_MakeThickSolid(myBody.Shape() , facesToRemove , \
-myThickness/50 , 1.e-3)
## Navoj na vratu steklenice:
#Ustvarjanje površine
aCyl1 = Geom_CylindricalSurface(gp_Ax3(neckAx2) , myNeckRadius * 0.99)
aCyl2 = Geom_CylindricalSurface(gp_Ax3(neckAx2) , myNeckRadius * 1.05)
#Definiranje 2D krivulje navoja
aPnt = gp_Pnt2d(2. * math.pi , myNeckHeight / 2.)
aDir = gp_Dir2d(2. * math.pi , myNeckHeight / 4.)
aAx2d = gp_Ax2d(aPnt , aDir)
aMajor = 2.0 * math.pi
aMinor = myNeckHeight / 10.
anEllipse1 = Geom2d_Ellipse(aAx2d , aMajor , aMinor)
anEllipse2 = Geom2d_Ellipse(aAx2d , aMajor , aMinor / 4.0)
anArc1 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse1), 0, math.pi)
anArc2 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse2), 0, math.pi)
anEllipsePnt2 = anEllipse1.Value(0.)
anEllipsePnt1 = anEllipse1.Value(math.pi)
aSegment = GCE2d_MakeSegment(anEllipsePnt1 , anEllipsePnt2)
#Robovi in mreže
aEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1.GetHandle() , aCyl1.GetHandle())
aEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment.Value() , aCyl1.GetHandle())
aEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2.GetHandle() , aCyl2.GetHandle())
aEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment.Value() , aCyl2.GetHandle())
#print (dir(aEdge1OnSurf1))
threadingWire1 = BRepBuilderAPI_MakeWire(aEdge1OnSurf1.Edge() , aEdge2OnSurf1.Edge())
threadingWire2 = BRepBuilderAPI_MakeWire(aEdge1OnSurf2.Edge() , aEdge2OnSurf2.Edge())
#3D predstava robov/mrež
breplib.BuildCurves3d(threadingWire1.Shape())
breplib.BuildCurves3d(threadingWire2.Shape())
#Površina navoja
aTool = BRepOffsetAPI_ThruSections(True)
aTool.AddWire(threadingWire1.Wire())
aTool.AddWire(threadingWire2.Wire())
aTool.CheckCompatibility(False)
myThreading = aTool.Shape()
## Izdelava sestava
aRes = TopoDS_Compound()
aBuilder = BRep_Builder()
aBuilder.MakeCompound (aRes)
aBuilder.Add (aRes, myBody.Shape())
aBuilder.Add (aRes, myThreading)
## Izris oblike
show_bottle(aRes)
def draw_bottle(event=None):
# Definiranje razdalj: širina, dolžina, višina
myWidth = 50.0
myThickness = 30.0
myHeight = 70.0
# Define Points
define_body(myWidth, myThickness, myHeight)
if __name__ == '__main__':
# OCC.Display.SimpleGui.init_display() returns multiple
# values which are assigned here
display, start_display, add_menu, add_function_to_menu = \
init_display()
draw_bottle() #kličemo podprogram za izris bottle
start_display()
}}}
=== Izris CAD prizme ===
{{{
#!python
## Izdelava prizme --primer bottle
from OCC.Display.SimpleGui import *
from OCC.BRepPrimAPI import *
from OCC.gp import *
from OCC.GC import *
from OCC.BRepBuilderAPI import *
from OCC.TopoDS import *
display, start_display, add_menu, add_function_to_menu = init_display()
myWidth = 50.0
myThickness = 30.0
myHeight = 70.0
#Definiranje začetnih točk
aPnt1 = gp_Pnt(-myWidth / 2. , 0 , 0)
aPnt2 = gp_Pnt(-myWidth / 2. , -myThickness / 4. , 0)
aPnt3 = gp_Pnt(0 , -myThickness / 2. , 0)
aPnt4 = gp_Pnt(myWidth / 2. , -myThickness / 4. , 0)
aPnt5 = gp_Pnt(myWidth / 2. , 0 , 0)
#Izdelava segmentov--definiranje geometrije
aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4)
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt4 , aPnt5)
#Izdelava robov -- definiranje topologije
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
#Povezovanje robov v mrežo
aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,\
aEdge3.Edge())
#Izdelava celotnega profila - mirror
xAxis = gp_OX()
aTrsf = gp_Trsf()
aTrsf.SetMirror(xAxis)
aBRepTrsf = BRepBuilderAPI_Transform(aWire.Shape() , aTrsf)
aMirroredShape = aBRepTrsf.Shape()
aMirroredWire = topods.Wire(aMirroredShape)
mkWire = BRepBuilderAPI_MakeWire()
mkWire.Add(aWire.Wire())
mkWire.Add(aMirroredWire)
myWireProfile = mkWire.Wire()
#Telo: Iz profila se izdela telo
myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile)
aPrismVec = gp_Vec(0 , 0 , myHeight)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face() , aPrismVec).Shape()
display.DisplayShape(myBody)
start_display()
}}}
== Povzetek prve vaje ==
=== Prikaz enostavnega izvoza STEP formata za eno obliko ===
{{{
#!python
# Vključimo knjižnico za izvoz STEP formata
from OCC.STEPControl import *
from OCC.BRepPrimAPI import *
# Pripravimo enostaven CAD primer za izvoz v STEP format
my_box_shape = BRepPrimAPI_MakeBox(50,50,50).Shape() # Izdelava kocke
# Obliko my_box_shape izvozimo v STEP format
step_writer = STEPControl_Writer()
step_writer.Transfer(my_box_shape, STEPControl_AsIs)
step_writer.Write("result_export_single.stp")
}}}
=== Prikaz enostavnega izvoza STEP formata za več oblik ===
{{{
#!python
# Vključimo knjižnico za izvoz STEP formata
from OCC.STEPControl import *
from OCC.BRepPrimAPI import *
# Pripravimo enostavna CAD primera za izvoz v STEP format
my_box_shape = BRepPrimAPI_MakeBox(50,50,50).Shape() # Izdelava kocke
my_sphere_shape = BRepPrimAPI_MakeSphere(20).Shape() # Izdelava krogle
# Obliki my_box_shape in my_sphere_shape izvozimo v STEP format
step_writer = STEPControl_Writer()
step_writer.Transfer(my_box_shape, STEPControl_AsIs)
step_writer.Transfer(my_sphere_shape, STEPControl_AsIs)
step_writer.Write("result_export_multi.stp")
}}}
=== Prikaz izvoza CAD modelov z barvami in layer.ji ===
{{{
#!python
# Vključimo knjižnico za izvoz STEP formata
from OCC.Utils.DataExchange.STEP import StepOCAF_Export
from OCC.BRepPrimAPI import *
# Pripravimo enostavna CAD primera za izvoz v STEP format
my_box_shape = BRepPrimAPI_MakeBox(50,50,50).Shape() # Izdelava kocke
my_sphere_shape = BRepPrimAPI_MakeSphere(20).Shape() # Izdelava krogle
# Export to STEP
my_step_exporter = StepOCAF_Export("result_export_multi_color_layer.stp") # Določitev imena file.a
my_step_exporter.set_color(1,0,0) # določitev barve (rdeča) -> RedGreenBlue paleta barv (RGB)
my_step_exporter.set_layer('red') # določitev layer.ja
my_step_exporter.add_shape(my_box_shape) # izbira oblike za izbrane parametre my_box_shape
my_step_exporter.set_color(0,1,0)
my_step_exporter.set_layer('green')
my_step_exporter.add_shape(my_sphere_shape)
my_step_exporter.write_file()
}}}
=== Pisanje datotek STL ===
Stereolitografski format je primeren za prikaz v spletnih pregledovalnikih (threejs z WebGL) saj
vključuje zapis v trikotnikih.
Ne vključuje barv in lahko izvozi le en Shape(). Da zmanjšamo velikost datoteke, namesto
ASCII izberemo binarni format z "False". Naslednji primer shrani enotsko kroglo:
{{{
#!python
# -*- coding: utf-8 -*-
from OCC.StlAPI import *
from OCC.BRepPrimAPI import *
my_sphere_shape = BRepPrimAPI_MakeSphere(1).Shape()
stl_writer = StlAPI()
stl_writer.Write(my_sphere_shape, "krogla.stl", False)
}}}
=== Uporaba funkcije zaokrožitve, pozicioniranje valja na izbrano mesto, združevanje CAD modelov ter izvoz v STEP format ===
V naslednjem primeru so prikazane naslednje funkcije:
* zaokrožitve
* pozicioniranje elementa
* združevanje CAD modelov
* enostaven izvoz CAD modela v STEP format
{{{
#!python
##Copyright 2011 Simon Kulovec (simon.kulovec@lecad.si)
##This file is part of pythonOCC.
## Importanje razlicnih knjiznic
# Uporabniski vmesnik GUI
from OCC.Display.SimpleGui import *
# OpenCascade
from OCC.gp import *
from OCC.TopoDS import *
from OCC.GC import *
from OCC.BRepBuilderAPI import *
from OCC.BRepPrimAPI import *
from OCC.BRepFilletAPI import *
from OCC.BRepAlgoAPI import *
from OCC.Utils.Topology import *
from OCC.BRep import *
from OCC.Utils.DataExchange.STEP import STEPExporter
# OCC.Display.SimpleGui.init_display() returns multiple
# values which are assigned here
display, start_display, add_menu, add_function_to_menu = \
init_display()
# Definiranje spremenljivk
myWidth = 50.0
myThickness = 30.0
myHeight = 70.0
# Definiranje zacetnih tock
aPnt1 = gp_Pnt(-myWidth / 2. , 0 , 0)
aPnt2 = gp_Pnt(-myWidth / 2. , -myThickness / 4. , 0)
aPnt3 = gp_Pnt(0 , -myThickness / 2. , 0)
aPnt4 = gp_Pnt(myWidth / 2. , -myThickness / 4. , 0)
aPnt5 = gp_Pnt(myWidth / 2. , 0 , 0)
# Definiranje geometrije
aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4)
aSegment1 = GC_MakeSegment(aPnt1 , aPnt2)
aSegment2 = GC_MakeSegment(aPnt4 , aPnt5)
# Definiranje topologije
aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value())
aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value())
aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value())
aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge() , aEdge2.Edge() ,\
aEdge3.Edge())
# Izdelava celotnega profila - mirror
xAxis = gp_OX()
aTrsf = gp_Trsf()
aTrsf.SetMirror(xAxis)
aBRepTrsf = BRepBuilderAPI_Transform(aWire.Shape() , aTrsf)
aMirroredShape = aBRepTrsf.Shape()
aMirroredWire = TopoDS_wire(aMirroredShape)
mkWire = BRepBuilderAPI_MakeWire()
mkWire.Add(aWire.Wire())
mkWire.Add(aMirroredWire)
myWireProfile = mkWire.Wire()
# Telo: Iz profila se izdela telo
myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile)
aPrismVec = gp_Vec(0 , 0 , myHeight)
myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face() , aPrismVec)
# Telo: Dodamo zaokrozitve (fillet)
mkFillet = BRepFilletAPI_MakeFillet(myBody.Shape())
topology_traverser = Topo(myBody.Shape())
for aEdge in topology_traverser.edges(): #z uporabo for zanke iščemo robove na CAD modelu in jih zaokrožujemo s funkcijo mkFillet in združujemo z .Add
mkFillet.Add(myThickness / 12. , aEdge) #velikost zaokrožitve myThickness / 12.
myBody = mkFillet.Shape() #vse zaokrožitve priključimo k prvotni obliki myBody
# Dodajanje grla na steklenico (valj)
neckLocation = gp_Pnt(0, 0, myHeight) #Določitev lokacije valja
neckNormal = gp_DZ() #smer normale, v katero bomo valj izvlekli
neckAx2 = gp_Ax2(neckLocation, neckNormal)
myNeckRadius = myThickness / 4 #radij valja
myNeckHeight = myHeight / 10 # višina valja
mkCylinder = BRepPrimAPI_MakeCylinder(neckAx2 , myNeckRadius , \
myNeckHeight)
myNeck = mkCylinder.Shape();
myBody = BRepAlgoAPI_Fuse(myBody, myNeck) #dodajanje valja k obliki myBody
# Izdelava sestava
aRes = TopoDS_Compound() #Določitev spremenljivke za sestav
aBuilder = BRep_Builder()
aBuilder.MakeCompound (aRes)
aBuilder.Add (aRes, myBody.Shape()) #Dodajanje različnih oblik v sestav aRes
# Export to STEP ()
my_step_exporter = STEPExporter("export_step_file.stp") #Določevanje imena STEP file.a
my_step_exporter.add_shape(aRes) #Dodajanje oblike v STEP file
my_step_exporter.write_file()
# Izris oblike
display.EraseAll()
print dir(display)
display.DisplayShape(aRes)
start_display()
}}}
== Povzetek druge vaje ==
=== Izdelava CAD kocke z predhodno definiranimi točkami (Uporaba for zanke za generiranje točk, površin, mreže) ===
{{{
#!python
import sys
from OCC.Display.SimpleGui import *
from OCC.gp import gp_Pnt
from OCC.GC import GC_MakeSegment
from OCC.BRepBuilderAPI import \
BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeWire,\
BRepBuilderAPI_MakeShell, BRepBuilderAPI_MakeSolid
from OCC.BRep import BRep_Builder
from OCC.TopoDS import TopoDS_Shell, TopoDS_Solid
from OCC import StlAPI
mesh = {
"vertices":[[-0.2,-0.2,0.2],[0.2,-0.2,0.2],[0.2,0.2,0.2],[-0.2,0.2,0.2],\
[-0.2,-0.2,0.6000000000000001],[0.2,-0.2,0.6000000000000001],\
[0.2,0.2,0.6000000000000001],[-0.2,0.2,0.6000000000000001]],
"faces":[[3,2,1,0],[4,5,6,7],[7,6,2,3],[5,4,0,1],[6,5,1,2],[4,7,3,0]]
}
def main():
vertices = [ gp_Pnt(p[0],p[1],p[2]) for p in mesh['vertices'] ]
oFaces = []
builder = BRep_Builder()
shell = TopoDS_Shell()
builder.MakeShell(shell)
for face in mesh['faces']:
edges = []
face.reverse()
for i in range(len(face)):
cur = face[i]
nxt = face[(i+1)%len(face)]
segment = GC_MakeSegment(vertices[cur],vertices[nxt])
edges.append(BRepBuilderAPI_MakeEdge(segment.Value()))
wire = BRepBuilderAPI_MakeWire()
for edge in edges:
wire.Add(edge.Edge())
oFace = BRepBuilderAPI_MakeFace(wire.Wire())
builder.Add(shell, oFace.Shape())
display.DisplayShape(shell)
if __name__ == '__main__':
display, start_display, add_menu, add_function_to_menu = \
init_display()
main()
start_display()
}}}
=== Branje vrednosti iz datoteke (input_file.dat) in generiranje CAD modela (Parametriziran CAD model) ===
Datoteka: input_file.dat
{{{
#!python
70 70 70
}}}
Programska koda (.py)
{{{
#!python
#Odpiranje datoteke input_file.dat, ter branje iz nje v izbrane spremenljivke
#Simon Kulovec
from OCC.Display.SimpleGui import *
from OCC.BRepPrimAPI import *
display, start_display, add_menu, add_function_to_menu = init_display()
#Branje iz datoteke: input_file.dat
f= open("input_file.dat", "r")
lines = f.readlines()
box0 = [] #Vektor v katerega shranjujemo prebrane vrednosti
#Stevilo prebranih vrstic je 1
for i in range(1):
x,y,z=[eval(s) for s in lines[i].split(" ")]
box0.append(x)
box0.append(y)
box0.append(z)
#Izpis prebranih vrednosti iz datoteke v terminal
print "%4.1f %4.1f %4.1f " % ( box0[i], box0[i+1],\
box0[i+2])
f.close()
my_box = BRepPrimAPI_MakeBox(box0[0],box0[1],box0[2]).Shape()
display.DisplayShape(my_box)
start_display()
}}}
=== Prikaz izdelave kocke in uporaba funkcije krožnega izvleka ===
{{{
#!python
##Prikaz izdelave kocke in uporaba funkcije krožnega izvleka
##S.Kulovec, 2011
from OCC.gp import *
from OCC.BRepPrimAPI import *
from OCC.TopExp import *
from OCC.TopAbs import *
import OCC.TopoDS
from OCC.BRep import *
from OCC.Geom import *
from OCC.GCE2d import *
from OCC.Geom2d import *
from OCC.BRepLib import *
from OCC.BRepFeat import *
from OCC.Utils.Topology import Topo
from OCC.BRepBuilderAPI import *
import sys, time
from OCC.Display.SimpleGui import *
display, start_display, add_menu, add_function_to_menu = init_display()
S = BRepPrimAPI_MakeBox(400.,250.,300.).Shape()
faces = list(Topo(S).faces())
F1 = faces[2]
surf = BRep_Tool_Surface(F1)
Pl = Handle_Geom_Plane_DownCast(surf)
D = gp.gp_OX()
MW1 = BRepBuilderAPI_MakeWire()
p1 = gp_Pnt2d(100.,100.)
p2 = gp_Pnt2d(200.,100.)
aline = GCE2d_MakeLine(p1,p2).Value()
MW1.Add(BRepBuilderAPI_MakeEdge(aline,surf,0.,p1.Distance(p2)).Edge())
p1 = gp_Pnt2d(200.,100.)
p2 = gp_Pnt2d(150.,200.)
aline = GCE2d_MakeLine(p1,p2).Value()
MW1.Add(BRepBuilderAPI_MakeEdge(aline,surf,0.,p1.Distance(p2)).Edge())
p1 = gp_Pnt2d(150.,200.)
p2 = gp_Pnt2d(100.,100.)
aline = GCE2d_MakeLine(p1,p2).Value()
MW1.Add(BRepBuilderAPI_MakeEdge(aline,surf,0.,p1.Distance(p2)).Edge())
MKF1 = BRepBuilderAPI_MakeFace()
MKF1.Init(surf,False)
MKF1.Add(MW1.Wire())
FP = MKF1.Face()
BRepLib_BuildCurves3d(FP)
MKrev = BRepFeat_MakeRevol(S,FP,F1,D,1,True)
F2 = faces[4]
MKrev.Perform(F2)
display.EraseAll()
display.DisplayShape(MKrev.Shape())
start_display()
}}}
== Napotki ==
Q: Zanima me kako se v PythonOCC dela luknje oz izvrtine(ali izreze). Katere funkcije se uporablja?
A: Podobno kot fuse je za izreze cut.
{{{
#!python
import OCC.Utils.Construct as construct
drzalo = construct.boolean_fuse(roka,nosilec) #celoten sestav
drzalo = construct.boolean_cut(drzalo, izvrtina)
}}}
[[Image(boolove-operacije.svg)]][[BR]]
Boolove operacije.
Q: Rad bi zaokrožil le robove profila, ne pa tudi prereza.
A: Če želimo zaokrožiti le določene robove lahko uporabimo različne teste.
Naslednji primer naredi kopijo stebra in v zanki preiskovalca topologije
dodaja robove, ki jih je potrebno zaokrožiti tako, da preverja višino začetne
in končne točke robu. V primeru, da točki nisti v ravnini (X,Y) ga zaokroži.
Velikost zaokrožitve je lahko največ tolikšna, da se še da normalno zaokrožiti
označene robove.
{{{
#!python
# Telo: Dodamo zaokrožitve (fillet)
mkFillet = BRepFilletAPI_MakeFillet(steber.Shape())
topology_traverser = Topo(steber.Shape())
for aEdge in topology_traverser.edges():
first, last = TopExp().FirstVertex(aEdge), TopExp().LastVertex(aEdge)
first_vert, last_vert = BRep_Tool().Pnt(first), BRep_Tool().Pnt(last)
if first_vert.Z() != last_vert.Z():
mkFillet.Add(1.2, aEdge)
display.DisplayShape(mkFillet.Shape())
}}}
Q: Kako postavimo model v drugi položaj?
A: Okoli osi v prostoru lahko rotirate:
{{{
#!python
LokacijaOsi1 = gp_Pnt(8, 45, -100)
RotacijaOs1 = gp_Ax1(LokacijaOsi1, gp_Dir(0,0,1)) #Os rotacije
TransfRot1 = gp_Trsf()
TransfRot1.SetRotation(RotacijaOs1, t1)
J19 = BRepBuilderAPI_Transform(J18, TransfRot1).Shape()
}}}
Translacija je še lažja:
{{{
#!python
Premik = gp_Vec(-130, -870, -2530)
Translacija = gp_Trsf()
Translacija.SetTranslation(Premik)
rezultat = BRepBuilderAPI_Transform(J17, Translacija).Shape()
}}}
Ostale funkcije so opisane v http://api.pythonocc.org/OCC.gp.gp_Trsf-class.html
=== Prikaz torus modela v brskalniku ===
[[Image(torus_webgl.PNG, width=480px, right)]]
{{{
#!/usr/bin/env python
##Copyright 2009-2014 Thomas Paviot (tpaviot@gmail.com)
##
##This file is part of pythonOCC.
##
##pythonOCC is free software: you can redistribute it and/or modify
##it under the terms of the GNU Lesser General Public License as published by
##the Free Software Foundation, either version 3 of the License, or
##(at your option) any later version.
##
##pythonOCC is distributed in the hope that it will be useful,
##but WITHOUT ANY WARRANTY; without even the implied warranty of
##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
##GNU Lesser General Public License for more details.
##
##You should have received a copy of the GNU Lesser General Public License
##along with pythonOCC. If not, see .
from OCC.Display.WebGl import threejs_renderer
from OCC.BRepPrimAPI import BRepPrimAPI_MakeTorus
torus_shp = BRepPrimAPI_MakeTorus(20., 10.).Shape()
my_renderer = threejs_renderer.ThreejsRenderer(background_color="#123345")
my_renderer.DisplayShape(torus_shp)
}}}
= Povezave in navodila za modeliranje z OCCT =
* [http://api.pythonocc.org PythonOCC API’s dokumentacija] na spletni strani
* [https://github.com/tpaviot/pythonocc-core/tree/master/examples Primeri PythonOCC]
* [raw-attachment:VisualizationOfGeometryWithUtilisingpythonOCC.pdf Vizualizacija s PythonOCC] -- v obliki PDF
* [raw-attachment:modalg.pdf Modelling algorithms za OpenCASCADE 6.6]
* [raw-attachment:modalg.pdf Modelling data]