AlICe

Research →
Teaching →
Media →
Title
Year
Relics of Electronic Hallucinations. Gazing at Early Computational Fluid Dynamics Drawings from Los Alamos Nuclear Research Center
2024
Architectural Analysis, Survey and Documentation of Built Heritage
2024
Rules, A Short History Of What We Live By, A book by Lorraine Daston
2024
Survey and characterisation of the archaeological landscape of Lovo
2024
Stoclet 1911 - Restitution
2024
Stoclet 1911 - Hypothesis
2024
TV Show: 3D Digitization and Built Heritage Preservation
2024
Drawing air: the evolution of the representation of air in architectural drawing from the industrial revolution to the present
2023
Architectural Analysis, Survey and Documentation of Built Heritage
2023
Analysis, Systems & Composition
2023
Code Tracing
2023
Maison du Peuple full scale experience on its original site
2023
From projection to building and vice versa
2023
Emergence of pre-digital algorithmic design
2023
Comparing Randomness
2023
Anthropic Units in Baroque Architecture, the Gallery of the Palazzo Spada and the Roman Palm
2023
Workshop Glyph
2023
Re-presentation as an analytical tool in Baroque Architecture
2022
Crossed Experimentations of Low-Altitude Surveys For The Detection Of Buried Structures
2022
Chamber Music Hall of Horta's Palais des Beaux-Arts: 3D Hypothesis
2022
Formal Analysis and Computer Process - Algorithmic Music II/II
2022
Misreading, once again...
2022
Perspectives on Dwelling : Architectural Anthropologies of Home
2022
Workshop (fig.22)
2022
Towards a multi-scale semantic characterization of the built heritage
2021
De l'incarnation de la protoarchitecture
2021
Formal Analysis and Computer Process - Algorithmic Music I/II
2021
Noise
2021
(Close) Reading Morphosis
2021
Jeu d’échelles / échelles du jeu
2021
[x] Pohlke
2021
Workshop (fig.)
2021
Perspectiva Virtualis
2021
Architectural Analysis, Survey and Documentation of Built Heritage
2020
Exploitation des numérisations pour l'analyse urbaine en contexte archéologique
2020
Formal Analysis and Computer Process - The Algorists
2020
Urban Planning Representation
2020
Projection built into Sketchpad III: origin of a critical field in computer graphics
2020
Architectural Analysis, Survey and Documentation of Built Heritage
2019
Exploitation de numérisations hétérogènes pour la représentation et l'analyse d'un site archéologique de grande échelle : Pachacamac 1532
2019
Relecture de vocabulaires d’architecture : apport de la complexité des représentations numériques dans la caractérisation de formes architecturales
2019
Victor Horta's Maison du Peuple 3D restitution hypothesis
2019
Architectural Analysis and Graphic Representation - Morphosis in the 1980s
2019
Formal Analysis and Computer Process - Medley II/II
2019
Histoires de Représentation
2019
Victor Horta's Maison du Peuple 3D restitution hypothesis
2019
Building Drawings : Decoding and Recoding the Graphic Projection Algorithm in Architectural Representation
2019
Places Royales Françaises. Réflexion d’une logique d’édification à travers une corrélation entre une analyse sémantique et un signal géométrique
2018
Virtual Systems – Actual Objects: Rendition of Morphosis ' Compositional Principles in the mid 1980s
2018
Formal Analysis and Computer Process - Medley I/II
2018

Pohlke

Blender Add-on to create canonical axonometric and oblique projection camera.

Download Link

"""
Create cameras in various standard parallel projection modes.
Copyright (C) 2021 Julien Rippinger

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import bpy
from bpy.app.handlers import persistent
from math import radians, cos


CAMERA_SETTINGS = {'axo.isometric':
                   {'rotation': (radians(54.74), 0.0, radians(45)),
                    'location': (10.0, -10.0, 10.0),
                    'pixel_ratio': 1},
                   'axo.dimetric':
                   {'rotation': (radians(60), 0.0, radians(23)),
                    'location': (5.53, -13.04, 8.18),
                    'pixel_ratio': 1},
                   'axo.trimetric':
                   {'rotation': (radians(67), 0.0, radians(34)),
                    'location': (8.59, -12.734, 6.52),
                    'pixel_ratio': 1},
                   'obl.military':
                   {'rotation': (radians(45), 0.0, radians(30)),
                    'location': (7.071, -12.247, 10*1/cos(radians(45))),
                    'pixel_ratio': 1/cos(radians(45))},
                   'obl.military.short':
                   {'rotation': (radians(45*.82), 0.0, radians(30)),
                    'location': (5.309, -9.195, 10*1/cos(radians(45))),
                    'pixel_ratio': 1/cos(radians(45*.82))},
                   'obl.cavalier':
                   {'rotation': (radians(45), 0.0, radians(45)),
                    'location': (10.0, -10.0, 10*1/cos(radians(45))),
                    'pixel_ratio': 1/cos(radians(45))},
                   'obl.cabinet':
                   {'rotation': (radians(45*.82), 0.0, radians(45)),
                    'location': (7.5, -7.5, 10*1/cos(radians(45))),
                    'pixel_ratio': 1/cos(radians(45*.82))},
                   'obl.cavalier.angle':
                   {'rotation':  (radians(15), 0.0, radians(30)),
                    'location': (10, -10, 10*1/cos(radians(45))),
                    'pixel_ratio': 1/cos(radians(15))}
                   }


def setup_camera(name):
    """
    core function creating camera with selected setups
    """
    bpy.ops.object.camera_add()
    obj = bpy.context.object
    scn = bpy.context.scene.render
    obj.data.type = 'ORTHO'
    obj.data.ortho_scale = 10
    obj.name = name
    obj.rotation_euler = CAMERA_SETTINGS[name]['rotation']
    x, y, z = bpy.context.scene.cursor.location
    u, v, w = CAMERA_SETTINGS[name]['location']
    obj.location = (x+u, y+v, z+w)
    scn.pixel_aspect_x = CAMERA_SETTINGS[name]['pixel_ratio']
    bpy.ops.view3d.object_as_camera()

# axonometry operators


class AlICe_OT_createisometriccam(bpy.types.Operator):
    bl_idname = "view3d.create_isometric_cam"
    bl_label = "isometric projection"
    bl_description = "create isometric camera"

    camera_name = 'axo.isometric'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


class AlICe_OT_createdimetriccam(bpy.types.Operator):
    bl_idname = "view3d.create_dimetric_cam"
    bl_label = "dimetric projection"
    bl_description = "create dimetric camera"

    camera_name = 'axo.dimetric'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


class AlICe_OT_createtrimetriccam(bpy.types.Operator):
    bl_idname = "view3d.create_trimetric_cam"
    bl_label = "trimetric projection"
    bl_description = "create trimetric camera"

    camera_name = 'axo.trimetric'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}

# oblique operators


class AlICe_OT_createmilitarycam(bpy.types.Operator):
    bl_idname = "view3d.create_military_cam"
    bl_label = "military projection"
    bl_description = "create military projection camera"

    camera_name = 'obl.military'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


class AlICe_OT_createmilitaryshortcam(bpy.types.Operator):
    bl_idname = "view3d.create_militaryshort_cam"
    bl_label = "military short projection"
    bl_description = "create military short projection camera"

    camera_name = 'obl.military.short'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


class AlICe_OT_createcavaliercam(bpy.types.Operator):
    bl_idname = "view3d.create_cavalier_cam"
    bl_label = "cavalier projection"
    bl_description = "create cavalier projection camera"

    camera_name = 'obl.cavalier'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


class AlICe_OT_createcabinetcam(bpy.types.Operator):
    bl_idname = "view3d.create_cabinet_cam"
    bl_label = "cabinet projection"
    bl_description = "create cabinet projection camera"

    camera_name = 'obl.cabinet'

    def execute(self, context):
        setup_camera(self.camera_name)
        return {'FINISHED'}


# tool panel


class AlICe_PT_projectionspanel(bpy.types.Panel):
    bl_idname = "POHLKE_PT_Camera_Panel"
    bl_label = "Add Cameras"
    bl_category = "Parallel Cams"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        # orthographic projections
        layout.label(text="Axonometry")
        layout.operator("view3d.create_isometric_cam",
                        text="Isometric")
        layout.operator("view3d.create_dimetric_cam",
                        text="Dimetric")
        layout.operator("view3d.create_trimetric_cam",
                        text="Trimetric")

        # oblique projections
        layout.label(text="Oblique")
        layout.operator("view3d.create_military_cam",
                        text="Military")
        layout.operator("view3d.create_militaryshort_cam",
                        text="Military shortened")
        layout.operator("view3d.create_cavalier_cam",
                        text="Cavalier")
        layout.operator("view3d.create_cabinet_cam",
                        text="Cabinet")


@persistent
def set_pixel_ratio(self, context):
    """
    change pixel ratio automatically before rendering
    """

    # get scene
    scn = bpy.context.scene.render
    # get active camera
    camera = bpy.context.scene.camera
    # change pixel ratio if necessary
    if camera.name.startswith('axo'):
        scn.pixel_aspect_x = 1
    # find oblique projection type
    elif camera.name.startswith('obl'):
        if camera.name.startswith('obl.military'):
            if camera.name.startswith('obl.military.short'):
                scn.pixel_aspect_x = CAMERA_SETTINGS['obl.military.short']['pixel_ratio']
            else:
                scn.pixel_aspect_x = CAMERA_SETTINGS['obl.military']['pixel_ratio']
        elif camera.name.startswith('obl.cavalier'):
            scn.pixel_aspect_x = CAMERA_SETTINGS['obl.cavalier']['pixel_ratio']
        elif camera.name.startswith('obl.cabinet'):
            scn.pixel_aspect_x = CAMERA_SETTINGS['obl.cabinet']['pixel_ratio']
    else:
        scn.pixel_aspect_x = 1


# register addon
bl_info = {
    "name": "Pohlke - Parallel Projection Cameras",
    "description": "Create cameras with various standard parallel projection modes",
    "author": "Julien Rippinger, Michel Lefèvre, alicelab.be",
    "version": (1, 1),
    "blender": (3, 0, 0),
    "location": "3D Viewport > Sidebar (N) > Parallel Cams",
    "warning": "",
    "doc_url": "https://codeberg.org/AlICe.lab/pohlke",
    "tracker_url": "https://codeberg.org/AlICe.lab/pohlke/issues",
    "category": "Camera",
}


classes = (AlICe_PT_projectionspanel,
           AlICe_OT_createisometriccam,
           AlICe_OT_createdimetriccam,
           AlICe_OT_createtrimetriccam,
           AlICe_OT_createmilitarycam,
           AlICe_OT_createmilitaryshortcam,
           AlICe_OT_createcavaliercam,
           AlICe_OT_createcabinetcam)

rgstr, unrgstr = bpy.utils.register_classes_factory(classes)


def register():
    rgstr()
    bpy.app.handlers.render_init.append(set_pixel_ratio)


def unregister():
    unrgstr()
    bpy.app.handlers.render_init.remove(set_pixel_ratio)


if __name__ == '__main__':
    register()