Skip to main content

ANYtk3D

Fast dependency-free 3D drawing on a Tkinter Canvas.

ANYtk3D is the standalone 3D viewport extracted from ANYstructure. It renders solids, stiffened plates, cylinders and arbitrary meshes directly on a tkinter.Canvas — no OpenGL, no matplotlib, only numpy and the standard library.

Features

  • Typical shapes out of the box — box, sphere, cone, truncated cone, cylinder, tube/pipe, torus, pyramid, wedge, prism, swept extrusion, disk/annulus, plane, arrow, ground grid, structural beam profiles (FB, T, I, L, C, BOX) and arbitrary index meshes
  • Directional lighting — ambient + Lambert diffuse + Blinn-Phong highlight, flat-shaded per face, two-sided, world-fixed or head-lamp
  • Correct depth order — every face is painted back to front by camera-space depth, closed solids cull their back faces, faces straddling the near plane are clipped instead of dropped, and 3D lines take part in the same sort
  • Layered transparency — 16 screen-door density steps with non-overlapping windows per layer, so a transparent shell shows its far wall, its contents and its near wall at once
  • Built for speed — geometry is compiled to numpy arrays once and every frame is vectorised; canvas items are pooled and only reconfigured when their appearance changes; the interactive level of detail self-tunes from measured frame times
  • Result fields and animation — a batched add_faces call for meshes coloured per element, plus frame capture and playback that adapts its detail to the requested frame rate
  • Camera orbit, zoom and pan; plate colour-coding by thickness (or any scalar) with a fixed legend; X/Y/Z axis overlay and optional rulers; animation caching and playback

Installation

pip install ANYtk3D

Tkinter ships with the standard CPython installers.

Quick start

import tkinter as tk
from anytk3d import Point3D, Tkinter3DCanvas

root = tk.Tk()
canvas = Tkinter3DCanvas(root, width=900, height=600, bg='white')
canvas.pack(fill='both', expand=True)

canvas.add_box(2.0, 1.0, 0.5, center=Point3D(-3, 0, 0.25), color='#4e79a7')
canvas.add_sphere(0.8, center=Point3D(0, 0, 0.8), color='#f28e2b')
canvas.add_tube(0.8, 0.5, 2.0, center=Point3D(3, 0, 1.0), color='#59a14f')
canvas.add_beam(Point3D(-4, 3, 0), Point3D(4, 3, 0), kind='I',
                web_height=0.6, flange_width=0.3)
canvas.add_grid(size_x=12, size_y=10)

canvas.fit_to_scene()
root.mainloop()

Shapes

add_box, add_box_from_bounds, add_sphere, add_cone, add_frustum, add_cylinder, add_tube, add_torus, add_pyramid, add_wedge, add_prism, add_extrusion, add_disk, add_plane, add_arrow, add_beam, add_grid, add_mesh, add_shape, plus the original add_polygon, add_line, add_text, add_rectangular_plate, add_flat_stiffener, add_flat_girder, add_longitudinal_stiffener and add_ring_stiffener.

Every shape builder takes center/axis placement and the same material keywords — color, outline, opacity, layer, cull_backface, lit, back_color, face_colors, tags.

The tessellation lives in anytk3d.shapes as plain functions returning a Mesh (vertices plus index faces), so it can be used, combined and tested without a display:

from anytk3d import shapes

mesh = shapes.torus(2.0, 0.4).merged(shapes.sphere(0.5))
canvas.add_shape(mesh, position=Point3D(0, 0, 3), color='#b07aa1')

Lighting

canvas.set_light(direction=Point3D(0.4, -0.6, 0.7), ambient=0.45,
                 diffuse=0.55, specular=0.12)
canvas.set_light(follow_camera=True)   # head lamp that orbits with the view
canvas.set_shading(False)              # flat colours, no shading

Ambient and diffuse sum to 1.0 by default, so a face turned toward the light renders in its exact base colour and every other face only darkens. That keeps colour-coded scenes matching their legend.

Transparency

opacity runs from 0 to 1 in 16 usable steps. A Tk canvas has no alpha channel, so this is screen-door stippling — but the front and back of a surface are given non-overlapping dither windows, sized so that stacking them reproduces ordinary alpha compositing (1 - (1 - a)^2). Passing an explicit stipple= string still uses that exact Tk pattern.

canvas.add_sphere(2.0, color='#7fb3d5', opacity=0.35)   # see the contents
canvas.add_box(1.0, 1.0, 1.0, color='#c0392b')          # ...through the shell

Colour scale

Plate colour-coding, the legend and thickness_color all share one scale. Replace it to use a different colour map — sample any ramp at a handful of positions and hand the stops over:

from matplotlib import colormaps, colors   # only if you want matplotlib maps
import anytk3d

stops = [(t / 16, colors.to_hex(colormaps['viridis'](t / 16))) for t in range(17)]
anytk3d.set_color_stops(stops)
anytk3d.get_color_stops()
anytk3d.reset_color_stops()                # back to the built-in blue→red ramp

Colours a canvas resolved itself (cylinder plate thickness, stiffener thickness) refresh on the next redraw. anytk3d.DEFAULT_COLOR_STOPS is the built-in scale; assigning to the old _THICKNESS_COLOR_STOPS constant has no effect — use set_color_stops.

Result fields

add_faces is the batched path for a mesh where every element carries its own colour — an FE stress plot, a deformed shape, a utilisation map. It takes the whole element set in one call and computes all the centroids and normals as array operations:

canvas.add_faces(polygons, colors=element_colors, outline='#64748b')
canvas.set_thickness_legend([0, 80, 160, 240], unit='MPa', title='von Mises')

polygons is a sequence of vertex sequences (Point3D or (x, y, z)), or an (faces, vertices, 3) array. On a 4900-element field this compiles in 4.5 ms against 48 ms for the same elements added one at a time.

Animation

Capture a scene per step, then replay it:

canvas.begin_animation_cache()
for step in range(frames):
    canvas.clear(keep_canvas=True)
    canvas.add_faces(deformed_shape(step), colors=field_colors(step))
    canvas.capture_animation_frame()
canvas.play_animation(fps=30)          # fast=None/True/False

A scene without cylinders or stiffeners compiles to a single shared representation, so each captured frame costs one build rather than two. Playback defaults to fast=None, which starts at full detail and drops to the reduced-detail path as soon as a frame overruns its slot; pass True or False to pin it. animation_frames, animation_frame_index and is_playing_animation are available for a progress readout.

Performance

Interactive frames use a reduced-detail scene with a face budget that adapts to the measured frame rate, so orbiting stays responsive on dense models and full detail returns when the mouse is released.

canvas.set_interactive_detail(4000)   # starting face budget while dragging
canvas.set_mesh_lines(False)          # drop per-face outlines
canvas.set_occlude_lines(False)       # keep 3D lines on top of geometry

Demos

The interactive showcase — shapes with live light controls, a colour-coded FE result field, animation playback with a measured frame rate, and a stiffened cylinder — is a single file you can run straight from an IDE (right-click → Run in PyCharm) or from a shell:

python -m anytk3d.demo

The original four-viewport demo is still there:

python -m anytk3d

or embed it in an existing Tk application with anytk3d.create_stiffened_cylinder_demo(root).

Relation to ANYstructure

ANYstructure uses this package for its 3D previews. The module keeps its original API (Tkinter3DCanvas, Point3D, Camera3D and every add_* method) so it stays a drop-in dependency.

Development

pip install -e .[dev]
pytest

License

GPL-3.0-or-later. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anytk3d-0.2.1.tar.gz (86.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anytk3d-0.2.1-py3-none-any.whl (78.4 kB view details)

Uploaded Python 3

File details

Details for the file anytk3d-0.2.1.tar.gz.

File metadata

  • Download URL: anytk3d-0.2.1.tar.gz
  • Upload date:
  • Size: 86.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for anytk3d-0.2.1.tar.gz
Algorithm Hash digest
SHA256 826ee1ffc06858eb4909907e243350e5e6a8f8b08f1360aa0c15114f7a732b10
MD5 62a06e1df5af1d7cd8a9243aa0da251f
BLAKE2b-256 d72fcf15308e074059ba72bed20170980a581b54d3e340075246ff4a1bd17533

See more details on using hashes here.

File details

Details for the file anytk3d-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: anytk3d-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 78.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for anytk3d-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 28fdaa86af68745f8dcdc6aa5fe3b8e996bd9a694c62642cffb1a8824a646772
MD5 1f66eaab151f4ba25dfe67c87f98d828
BLAKE2b-256 f65487d3ea930ec3ceac28dc5f5b3c740bfa852cb35b8019743329e40c3f2c6f

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.3

2 files

0.5.2

2 files

0.5.0

2 files

0.4.0

2 files

0.2.3

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page