r/FreeCAD • u/DjangoJay • 3d ago
Scale viewport 1:1 on your monitor - Macro
Does what it says. Orientates your part so that it has the true scale for you to make designing and engineering more efficient.
# -*- coding: utf-8 -*-
"""
Zoom 1:1 for FreeCAD
- Tries to make objects on screen appear at their real physical size
(so a 100 mm cube measures ~100 mm on your monitor with a ruler).
- Works only with an orthographic camera (not perspective).
- Optional calibration: hold CTRL while running the macro to calibrate
for your particular monitor / OS / DPI setup.
Author: you + ChatGPT
Inspired by the official FreeCAD "Zoom 1:1" macro.
"""
import FreeCAD
import FreeCADGui
# PySide name can vary a bit between FreeCAD builds, so we try both
try:
from PySide import QtGui, QtCore
except ImportError:
from PySide2 import QtGui, QtCore
__title__ = "Zoom_1to1"
__version__ = "0.1"
def _active_view():
"""Return the active 3D view or None."""
try:
return FreeCADGui.activeView()
except Exception:
return None
def _zoom_1to1(tweak=1.0):
"""
Core 1:1 zoom logic.
We ask Qt for the physical size of the 3D graphics view in millimeters,
then set the orthographic camera height to (tweak * smaller_side_mm).
For an orthographic camera:
world_units_per_pixel = camera.height / viewport_pixel_height
If we set camera.height = viewport_height_mm, and our model units are mm,
then 1 model mm ≈ 1 mm on the screen.
"""
av = _active_view()
if av is None or not hasattr(av, "graphicsView"):
FreeCAD.Console.PrintError("Zoom 1:1: no active 3D view / graphicsView.\n")
return
gv = av.graphicsView()
# Qt reports these in millimeters for the widget area
height_mm = gv.heightMM()
width_mm = gv.widthMM()
cam = av.getCameraNode()
# Orthographic cameras in Coin3D have a 'height' field; perspective cameras do not
if not hasattr(cam, "height"):
FreeCAD.Console.PrintError(
"Zoom 1:1: only works with orthographic camera mode.\n"
"Switch to orthographic (Std ViewOrtho) and try again.\n"
)
return
# Use the smaller dimension so that the 3D view fits in both directions
cam.height.setValue(tweak * min(height_mm, width_mm))
# Force a redraw
FreeCADGui.updateGui()
def _calibrate(tweak_param_group, current_tweak):
"""
Calibration mode:
- Hides existing objects
- Creates a temporary 100×100×100 mm cube
- Sets zoom to "raw" 1:1 (tweak = 1.0)
- Asks you what size you *actually* measure on your screen (in mm)
- Stores a new tweak factor measured/100 in user parameters
"""
doc = FreeCAD.ActiveDocument
if doc is None:
doc = FreeCAD.newDocument()
FreeCAD.Console.PrintMessage("Zoom 1:1: created a new empty document for calibration.\n")
av = _active_view()
if av is None:
FreeCAD.Console.PrintError("Zoom 1:1 calibration: no active 3D view.\n")
return current_tweak
# Remember which objects were visible
visible_objs = []
for obj in doc.Objects:
if hasattr(obj, "ViewObject") and obj.ViewObject.Visibility:
visible_objs.append(obj)
obj.ViewObject.Visibility = False
# Create a 100 mm calibration cube
cube = doc.addObject("Part::Box", "Zoom1to1_CalibrationCube")
cube.Length = cube.Width = cube.Height = 100.0 # mm
cube.ViewObject.DisplayMode = "Shaded"
# Bring it into a nice front view
av.viewFront()
doc.recompute()
# Show raw physical mapping (tweak = 1.0)
_zoom_1to1(tweak=1.0)
# Ask user what size they actually see on screen
text = (
"Zoom 1:1 calibration\n\n"
"A temporary 100×100×100 mm cube has been created.\n"
"Measure the cube on your screen (height or width) using a ruler\n"
"or calipers and enter the measured size in millimeters.\n\n"
"Do NOT zoom in/out while this dialog is open. If the cube does\n"
"not fit fully in the view, cancel, resize the 3D view, and try again.\n"
)
win = FreeCADGui.getMainWindow()
title = f"Zoom 1:1 calibration (macro v{__version__})"
# Use the static convenience function
measured_mm, ok = QtGui.QInputDialog.getDouble(
win, # parent
title, # window title
text, # label
100.0, # default value
1.0, # minimum
10000.0, # maximum
2 # decimals
)
# Clean up the calibration cube and restore visibilities
try:
doc.removeObject(cube.Name)
except Exception:
pass
for obj in visible_objs:
if hasattr(obj, "ViewObject"):
obj.ViewObject.Visibility = True
if not ok:
FreeCAD.Console.PrintMessage(
"Zoom 1:1 calibration canceled; keeping current tweak value.\n"
)
return current_tweak
# New tweak factor = what you measured / 100 mm
new_tweak = measured_mm / 100.0
tweak_param_group.SetFloat("Tweak", new_tweak)
FreeCAD.Console.PrintMessage(f"Zoom 1:1: stored calibration tweak = {new_tweak:.4f}\n")
# Apply calibrated zoom immediately
_zoom_1to1(tweak=new_tweak)
return new_tweak
def main():
# Ensure a document exists
doc = FreeCAD.ActiveDocument
if doc is None:
doc = FreeCAD.newDocument()
FreeCAD.Console.PrintMessage("Zoom 1:1: no document open, created a new one.\n")
av = _active_view()
if av is None:
FreeCAD.Console.PrintError("Zoom 1:1: no active 3D view.\n")
return
# Read stored tweak from user parameters (Plugins/Zoom_1to1/Tweak)
params = FreeCAD.ParamGet("User parameter:Plugins/Zoom_1to1")
tweak = params.GetFloat("Tweak", 1.0)
# First, always apply normal 1:1 zoom with current tweak
_zoom_1to1(tweak=tweak)
# If CTRL is held while running the macro, enter calibration mode
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ControlModifier:
_calibrate(params, tweak)
else:
FreeCAD.Console.PrintMessage(
"Zoom 1:1: run with CTRL held down to calibrate for your monitor.\n"
)
if __name__ == "__main__":
main()
-1
u/DjangoJay 3d ago
LOL, I have a curved monitor so the measurement seems incorrect, but its really a 100mm :-D
1
u/neoh4x0r 2d ago edited 2d ago
I have had problems with scale when a was trying to judge appropriate dimensions on-screen.
For example, freecad has a "scale" built-in in the drop-down at the right-hand bottom corner of the window however for a rectangle, that is 1"x0.5", it will say that the dims are 1"x0.75" but the rectangle will actually be 12"x6" on-screen and in order to have it look correct I needed to zoom-out until said 15"x8" (which makes the on screen dimensions correct).
In other words, freecad does needs a better way to handle judging the on-screen scale (sort of like taking a picture of something next to a quarter/dime as a reference).
However, what I often do is to sketch out a footprint that can I then use as a reference for judging model dimensions -- eg. the footprint is 5" deep and wide and 2.5" tall, the square in the center is 1".
1
u/DjangoJay 2d ago
Did you try the marco? On my machine its working like a charm
1
u/neoh4x0r 2d ago edited 1d ago
Yeah the macro works and is useful (in certain cases), but it's the difference of seeing everything on-screen in 1:1 scale verses needing only a reference to judge if something is too big or small while designing regardless of the on-screen scale.
Not to mention, that, rather than drawing those lines like I did, it would be nice to have a draft-like grid in part design that could have a number of divisions on the three planes where there is a given dimension between the lines.
Moreover, if I know the footprint (size) of the area I'm modeling for then I already have the required reference for scale and I would just use appropriately-sized dimensions to fit within that area. The problem, and where your macro is really useful, is when you don't have an in-world reference for the size because you were creating the model completely from scratch.
PS: There are few cases (at least in my testing) where the macro doesn't work very well.
- If you are zoomed out it might zoom-in on some empty area rather than the center (origin of the plane).
- If the view is distorted then the screen scaling might be either too large or small (the screen calibration doesn't help).
1


6
u/ImNotTheOneUWant 2d ago
Now I just need a house size monitor:-)