Plot 2D

# Copyright (c) 2018-2025 William Emerison Six
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

Problem 1

import math

import sympy

from modelviewprojection.mathutils import (
    Vector1D,
    Vector2D,
    Vector3D,
    compose,
    compose_intermediate_fns,
    identity,
    inverse,
)
from modelviewprojection.mathutils import rotate as R
from modelviewprojection.mathutils import scale_non_uniform_2d as S
from modelviewprojection.mathutils import translate as T
from modelviewprojection.nbplotutils import (
    create_basis,
    create_graphs,
    create_unit_circle,
    create_x_and_y,
    draw_isoceles_triangle,
    draw_right_triangle,
    draw_screen,
    draw_second_right_triangle,
)
T(Vector1D(5))
\[T_{<[5]>}\]
S(5, 6)
\[S_{<5,6>}\]
inverse(T(Vector1D(5)))
\[T_{<[-5]>}\]
T(Vector2D(5, 6))
\[T_{<[5, 6]>}\]
T(Vector3D(5, 6, 7))
\[T_{<[5, 6, 7]>}\]
inverse(T(Vector3D(5, 6, 7)))
\[T_{<[-5, -6, -7]>}\]
R(sympy.pi / 2)
\[R_{<\frac{\pi}{2}>}\]
compose([R(sympy.pi / 2), T(Vector2D(5, 6))])
\[R_{<\frac{\pi}{2}>} \circ T_{<[5, 6]>}\]
inverse(compose([R(sympy.pi / 2), T(Vector2D(5, 6))]))
\[T_{<[-5, -6]>} \circ R_{<- \frac{\pi}{2}>}\]

Draw graph paper

Just draw graph paper, where one unit in the x direction is blue, and one unit in the y direction is pink. The graph paper corresponds to the numbers on the left and on the bottom.

fn = R(math.radians(53.130102))
with create_graphs(graph_bounds=(5, 5)) as axes:
    create_basis(fn=fn)
    create_x_and_y(fn=fn)
    create_unit_circle(fn=fn)
    axes.set_title(fn._repr_latex_())
images/025086f46518961c29daf8ca7f6ab04909a54f92d2533e329a4316813f90b990.svg

Draw relative graph paper

Draw two relative number lines, making a relative graph paper, but keep the original coordinate system on the left and bottom. Any point in the plane can be described using two different graph papers.

fn = R(math.radians(53.130102))
with create_graphs(graph_bounds=(5, 5)) as axes:
    create_basis(fn=R(0.0))
    create_x_and_y(fn=R(0.0))
    create_basis(
        fn=fn,
        xcolor=(0, 1, 0),
        ycolor=(1, 1, 0),
    )
    create_x_and_y(
        fn=fn,
        xcolor=(0, 1, 0),
        ycolor=(1, 1, 0),
    )
    create_unit_circle(fn=fn)
    axes.set_title(fn._repr_latex_())
images/7aa86deb8a055d2faa7b54c5a719506f764f883b6809488a1730cc67184b87e7.svg

Draw relative graph paper

Draw two relative number lines, making a relative graph paper, but keep the original coordinate system on the left and bottom. Any point in the plane can be described using two different graph papers.

fn = R(math.radians(53.130102))
with create_graphs(graph_bounds=(5, 5)) as axes:
    create_basis(fn=R(0.0))
    create_x_and_y(fn=R(0.0))
    create_basis(
        fn=fn,
        xcolor=(0, 1, 0),
        ycolor=(1, 1, 0),
    )
    create_x_and_y(
        fn=fn,
        xcolor=(0, 1, 0),
        ycolor=(1, 1, 0),
    )
    create_unit_circle(fn=fn)
    draw_right_triangle()
    draw_second_right_triangle()
    axes.set_title(fn._repr_latex_())
images/4a39e6fa0253e29e89939c5c2068003b8b1b7a4b3ed34e4d7f760f6088fef908.svg

Draw relative graph paper, defined by composed functions

Draw a translated and rotated graph paper. You can read the sequence of composed functions in the order that they are applied, or in reverse order

fn = compose(
    [
        R(sympy.pi / 4),
        T(Vector2D(x=2.0, y=0.0)),
    ]
)
with create_graphs() as axes:
    create_basis(
        fn=fn,
    )
    create_x_and_y(fn=fn)
    create_unit_circle(fn=fn)
    axes.set_title(fn._repr_latex_())
images/5549f35eada83c3bcc6bb9ef822f7427f642c89e9a6c44905531edebc33bd13c.svg

Composed functions, read bottom up

The sequence of functions shown, where the translate is applied first, and operations are relative to the units on the left and bottom.

for f in compose_intermediate_fns([R(sympy.pi / 4), T(Vector2D(x=2.0, y=0.0))]):
    # TODO - figure out if I can render the latex as part of one markdown command,
    # if I were to uncomment out this line and other markdown lines,
    # the build of HTML would fail

    with create_graphs() as axes:
        create_basis(fn=f)
        create_x_and_y(fn=f)
        create_x_and_y()
        draw_isoceles_triangle(fn=f)
        create_unit_circle(fn=f)
        create_unit_circle()
        axes.set_title(f._repr_latex_())
images/ed56ceaf2317632b52f947ec2c576fce538207e578d944c2d9fe7c39f3590820.svg images/ce241d9f12f8852b6c4c7979349b2cc89086b0042d5bfc8ebe21996da9937ab4.svg images/bd2688c7b626a68061fc09d610f45f121186d0290e4485a59fffc80563c6672a.svg

Composed functions, read top down

The sequence of functions shown, where we visualize the rotate first, and then the translate relative to that relative graph paper.

for f in compose_intermediate_fns(
    [
        R(sympy.pi / 4),
        T(Vector2D(x=1.0, y=0.0)),
    ],
    relative_basis=True,
):
    with create_graphs() as axes:
        create_basis(fn=f)
        create_x_and_y(fn=f)
        draw_isoceles_triangle(fn=f)
        create_unit_circle(fn=f)
        axes.set_title(f._repr_latex_())
images/fdccb5d6e74b46f1f3775c53bbbf5896ef8d9edb2bab72bfd6f3ffc741549649.svg images/be6297f670a2daed3a3649e35abed14f362f04568f3d99cb588eba63e272d2dd.svg images/ebe9a1eb1ee9d4e2e4271dd8e5be91e8062fe46395f14a39d8f716e6ac41d7aa.svg
screen_width: int = 4
screen_height: int = 3

for f in compose_intermediate_fns(
    [
        T(Vector2D(-0.5, -0.5)),
        S(screen_width, screen_height),
        S(0.5, 0.5),
        T(Vector2D(x=1.0, y=1.0)),
    ],
    relative_basis=False,
):
    with create_graphs(graph_bounds=(6, 6)) as axes:
        # create_basis(fn=f)
        # create_x_and_y(fn=f)
        create_basis(fn=identity())
        create_x_and_y(fn=identity())
        # draw_ndc(fn=f)
        draw_screen(width=screen_width, height=screen_height, fn=f)
        axes.set_title(f._repr_latex_())
images/720c14b79761cd75d5b17ba96a6ce55a03ab9c6ba71da1a1c4cbba0c1c16f24d.svg images/fdb1bb6c7a0ada3b77c36784b76f70e6f645100d3cc33958493d3fe6fbae5859.svg images/59e81d8fb88fd311e3e7362f4f5d28961af4002d5c58b7d589224488ab3b673e.svg images/37179cec485de3e89b39cd5c82f4e9ca93e6e2d1364d9efb3b8a6be1811967d3.svg images/17db0c5bff9a22868ef70f492779fb201331bc881ebf409f2635adcddf2b61fa.svg
screen_width: int = 4
screen_height: int = 3

for f in compose_intermediate_fns(
    [
        T(Vector2D(-0.5, -0.5)),
        S(screen_width, screen_height),
        S(0.5, 0.5),
        T(Vector2D(x=1.0, y=1.0)),
    ],
    relative_basis=True,
):
    with create_graphs(graph_bounds=(6, 6)) as axes:
        # create_basis(fn=f)
        # create_x_and_y(fn=f)
        create_basis(fn=identity())
        create_x_and_y(fn=f)
        # draw_ndc(fn=f)
        draw_screen(width=screen_width, height=screen_height, fn=f)
        axes.set_title(f._repr_latex_())
images/01cdee7fc8f52c80d3e21ccf0ae529cddae0e0b4d4b10f7d9168667ae362bb01.svg images/9c0284e54ac6df43032ffba9585e37db4fd4df4c88fcf3fe294aa233fd60b8b7.svg images/0194b07954cedb7fbdb4a86569f68c59a2ec605fcf37b3a33ba8b621a0850de6.svg images/fbc7ffc4fe49e4b626eb587fbb5af54c08c08eb5ffbb01b2a1c8a512f111dbaf.svg images/cf024ce673a7d5cd78bef230667dc3d4f06f459170e60d6acadf2be6d8703773.svg