• Людям нужны не компьютеры и не программы, им нужно решение их задач.

Калькулятор треугольников

Предложение поступило от учеников:
— А давайте напишем калькулятор треугольников, а то они в интернете все какие-то кривые.
— А давайте! Только для полезности и простоты — напишем калькулятор для прямоугольных треугольников.
— У-у…
— Ну, во-первых, калькулятор прямоугольных треугольников сможет считать еще синусы и косинусы углов.
— Как это?
— А из определения синусов и косинусов.
— …
— Нет, не из того, которое в учебниках, а из того, которое отражает суть самих синусов и косинусов. Смотрите: к чему удобнее всего привести любую числовую величину?
— К нулю!
— Да. Только потом с этим нулём уже ничего не сделаешь. А если взять из жизни? Например, размер?
— К единице!
— Правильно. Самое главное, что на единицу делить — удобнее всего. Именно так и рассуждали древние математики.

Первое — в подобных треугольниках все соответствующие углы равны, а соотношения длин сторон — неизменны. Следовательно, углы в треугольнике определяются соотношением длин сторон, а не их значениями.

Второе — любой треугольник можно представить в виде пары прямоугольных треугольников.

Что отсюда следует? Что научившись «считать» прямоугольные треугольники, мы сможем «считать» любые треугольники.

Далее: для того, чтобы научиться «считать» прямоугольные треугольники любых размеров, достаточно научиться «считать» приведённые прямоугольные треугольники. И в качестве опорного размера решили взять длину гипотенузы. Её привели к единице, и тогда длины катетов — назвали синусом и косинусом соответствующего угла. И всё!
— Так просто?
— Да нет, пожалуй, всё же не слишком просто. Однако, это чуть лучше, чем обычные определения, что синус — это отношение катета к гипотенузе. Хотя, это именно так и есть. И если в этой формуле длину гипотенузы принять за единицу, мы как раз и получим, что синус — это длина катета.

Итак, мы будем «считать» прямоугольные треугольники.

Сначала было предложение сделать всё это в графическом интерфейсе, но прикинув временные затраты на создание интерфейса в MS Small Basic, мы всё-таки вернулись к консоли Windows.

На первом же занятии мы разработали интерфейс программы. Нельзя назвать его отличным, однако, он получился вполне функциональным.

Затем, уже на кружке по программированию был разработан строчный редактор для ввода числовых значений. При его разработке мы воспользовались уже созданной нами программой вывода кодов клавиатуры.

После этого мы дружно проработали: во-первых, математический аппарат — все тригонометрические формулы для вычислений, а во-вторых, общий алгоритм работы программы. Главный упор был сделан на то, что программа должна автоматически вычислять всё, что не введено пользователем.

Затем в течение примерно месяца мы реализовали сам алгоритм и внесли в него ряд дополнений, в частности, различный цвет полей: чёрный — введённое пользователем — основа для расчётов, синий — результаты вычислений, серый — пустые, невычисленные или неподтверждённые поля (поля с недостоверной информацией). Для удобства работы с цветами в консоли — также была дополнительно написана небольшая программка.

В итоге получился довольно простой и удобный для пользователя калькулятор, автоматически «считающий» заданные любым способом прямоугольные треугольники.

Для работы с программой после её запуска нужно стрелочками «вверх» или «вниз» на клавиатуре выбрать поле для ввода числового значения, ввести его, используя клавиши с цифрами и точками (для дробных значений) и подтвердить ввод, нажав клавишу Enter. В случае, если введён только один угол — сразу же будет посчитан второй угол.

При этом значения автоматически проверяются на корректность вводимые величины: углы — от 0 до 90, длины — больше 0. В случае ошибки они заменяются на ближайшее корректное.

Если попытаться ввести три исходных значения, останется только одно — последнее введённое, потому что не понятно, какие два из этих трёх выбрать для расчётов.

Подтвердить уже существующее серое или синее значение в качестве введённого пользователем можно, нажав в соответствующем поле клавишу Enter.

Если вы просто «ушли» стрелками из поля во время его редактирования — в нем восстановится предыдущее значение.

В программе использована библиотека LitDev.

У программы, на мой взгляд, множество недочётов (об этом даже говорят некоторые комментарии в коде), но она вполне приемлема в качестве серьёзного учебного проекта. Конечный результат вполне можно назвать продуктом: он прост, работоспособен, не имеет принципиальных ошибок в работе. Если вы хотите просто скачать готовую программу для использования — вы можете это сделать по этой ссылке.

На этом проекте учащиеся научились работать в группе: обилие независимых подзадач позволило решать их параллельно разным людям, при этом решение общих вопросов выполнялось всеми. Кроме того, был закреплён материал по тригонометрии. А самое главное — многие поняли, что даже самые привычные функции и элементы интерфейса можно реализовать самостоятельно, без всяких библиотек — «вручную» и без особых проблем.

 

Sub Code ' подпрограмма проверки кода на цифру
    dn = ""
    For ni=0 To 9 ' ищем код цифры в массиве
        If K = dk[ni][1] Or K = dk[ni][2] Then ' цифра
            dn = ni
        EndIf
    EndFor
    
    If dn = "" And ( K = "Decimal" Or K = "OemQuestion" Or K = "OemPeriod" Or K = "Oemcomma" ) Then ' точка
        dn = "."
    EndIf
    
    If dn = "" Then ' не цифра
        dn = "False"
    EndIf
EndSub
'____________________________________________________________________________

Sub WriteF ' подпрограмма вывода текущего поля
    TextWindow.CursorTop = fd[cf][2] ' ставим в строку вывода
    For ij=fd[cf][1] To fd[cf][1]+21 ' заполняем поле пробелами = закрашиваем его фоном
        TextWindow.CursorLeft=ij
        TextWindow.Write(" ")
    EndFor
    TextWindow.ForegroundColor = fd[cf][4] ' ставим цвет символов ' ставим синий !
    TextWindow.CursorLeft = fd[cf][1] ' в начало строки
    
    If Text.GetLength(fd[cf][3])>21 Then ' если очень большое значение - проверяется при вводе - можно убрать!
        fd[cf][3] = Text.GetSubText(fd[cf][3],1,20)
        str = Text.Append("~",fd[cf][3])
        TextWindow.Write(str) ' выводим
    Else
        TextWindow.Write(fd[cf][3]) ' выводим
    EndIf
    
    If fd[cf][3] = "" Then ' если пустая
        TextWindow.CursorLeft = fd[cf][1] ' в начало строки
        TextWindow.ForegroundColor = 8 ' цвет - серый
        TextWindow.Write("0") ' и выводим 0
        TextWindow.CursorLeft = fd[cf][1] ' в начало строки
    EndIf
EndSub
'______________________________________________________________

LDTextWindow.KeyDown = Keyb ' назначение обработчика клавиатуры

Sub Keyb ' обработчик клавиатуры
    K = LDTextWindow.LastKey ' код последней нажатой клавиши
    Code() ' попытались превратить код клавиши в число
    
    If dn <> "False" Then ' цифра - работаем с текущим полем
        If dn= "." And ( Text.IsSubText(fd[cf][3],dn) Or Text.IsSubText(fd[cf][3],",") ) Then ' если точка уже есть - запятую можно убрать!
            dn= "" ' сбрасываем её
        EndIf
        If Text.GetLength(fd[cf][3]) < 21 Then ' проверяем длину строки - и если можно...
            fd[cf][3] = Text.Append(fd[cf][3], dn) ' пишем в строку
        EndIf
        EntFl = 0
        fd[cf][4] = 8 ' цвет!!!
        WriteF() ' выводим строку
    Else '  не цифра
        If K = "Delete" Then ' если нажали Del - очистка поля без подтверждения
            EntFl = 1 ' без подтверждения нажатием Enter !
            fd[cf][3] = "" ' удаляем строку до ""
            fd[cf][4] = 8 ' поле становится серым
            WriteF() ' выводим строку
        ElseIf K = "Return" Then ' если нажали Enter - ввод поля - без перехода к следующему!
            EntFl = 1 ' ставим флаг
            oldstr = fd[cf][3]
            fd[cf][4] = 0 ' поле становится чёрным
            Calc() ' здесь надо посчитать! (если можно)
            Calc() ' на всякий случай ещё раз - хуже не будет
        ElseIf K = "Up" Then ' стрелка вверх
            If EntFl = 0 Then ' если Enter не нажимали
                fd[cf][3] = oldstr ' восстановить старое значение
                fd[cf][4] = oldcl ' восстановить цвет!!!
                WriteF() ' перевывести строку
            Else
                EntFl = 0 ' сбрасываем флаг
            EndIf
            
            cf = cf - 1
            If cf < 1 Then
                cf = 5
            EndIf
            oldcl = fd[cf][4] ' сохранить цвет
            oldstr = fd[cf][3]
            TextWindow.CursorTop = fd[cf][2]
            If Text.GetLength(fd[cf][3]) > 21 Then
                TextWindow.CursorLeft = fd[cf][1] + 21 ' переставить курсор в конец текста
            Else
                TextWindow.CursorLeft = fd[cf][1] + Text.GetLength(fd[cf][3]) ' переставить курсор в конец текста
            EndIf
        ElseIf K = "Down" Then ' стрелка вниз
            If EntFl = 0 Then ' если Enter не нажимали
                fd[cf][3] = oldstr ' восстановить старое значение
                fd[cf][4] = oldcl ' восстановить цвет!!!
                WriteF() ' перевывести строку
            Else
                EntFl = 0 ' сбрасываем флаг
            EndIf
            
            cf = cf + 1
            If cf > 5 Then
                cf = 1
            EndIf
            oldcl = fd[cf][4] ' сохранить цвет
            oldstr = fd[cf][3]
            TextWindow.CursorTop = fd[cf][2]
            If Text.GetLength(fd[cf][3]) > 21 Then
                TextWindow.CursorLeft = fd[cf][1] + 21 ' переставить курсор в конец текста
            Else
                TextWindow.CursorLeft = fd[cf][1] + Text.GetLength(fd[cf][3]) ' переставить курсор в конец текста
            EndIf
        ElseIf K = "Back" Then ' нажали BackSpace
            EntFl = 0 ' сбрасываем флаг
            fd[cf][3] = Text.GetSubText(fd[cf][3],1,Text.GetLength(fd[cf][3])-1) 'один символ с конца удаляем
            fd[cf][4] = 8 ' цвет!!!
            WriteF() ' выводим строку
        ElseIf K = "Escape" Then ' выход
            TextWindow.BackgroundColor = 0
            For i=1 To 15
                TextWindow.ForegroundColor = i
                TextWindow.CursorLeft = 0
                TextWindow.CursorTop = 20
                TextWindow.WriteLine("                                  До свидания!")
                Program.Delay(100)
            EndFor
            Program.End()
        Else
            ' ничего
        EndIf
    EndIf
EndSub
'____________________________________________________________________________

Sub Calc ' подпрограмма вычисления и вывода
    bln = 0 ' количество полей с исходными данными
    For i = 1 To 5 ' обходим все поля
        If fd[i][4] = 0 Then ' определяем, какие чёрные
            bln = bln + 1 ' считаем, сколько чёрных
        EndIf
    EndFor
    
    ' Проверка - заданный угол не может быть >= 90, <=0 - ставим в край диапазона!
    ' заданные расстояния не могут быть <=0 - делаем их = 1
    For i=1 To 5
        If fd[i][4] = 0 And fd[i][3] <= 0 Then ' если задана величина она <= 0
            fd[i][3] = 1 ' ставим её в 1
        EndIf
    EndFor
    
    ' заданный угол не может быть >= 90 - ставим в край диапазона!
    If fd[4][4] = 0 And fd[4][3] >= 90 Then ' если задан угол А и он >= 90
        fd[4][3] = 89 ' ставим его в 89
        fd[5][3] = 1
    EndIf
    If fd[5][4] = 0 And fd[5][3] >= 90 Then ' если задан угол B и он >= 90
        fd[5][3] = 89 ' ставим его в 89
        fd[4][3] = 1
    EndIf
    
    If bln = 1 Then ' если введён только один угол
        If fd[4][4] = 0 Then
            fd[5][3] = 90 - fd[4][3] ' считаем другой
            fd[5][4] = 1 ' синий
            For i=1 To 3
                fd[i][4] = 8 ' остальные - серый
            EndFor
        ElseIf fd[5][4] = 0 Then
            fd[4][3] = 90 - fd[5][3] ' считаем другой
            fd[4][4] = 1 ' синий
            For i=1 To 3
                fd[i][4] = 8 ' остальные - серый
            EndFor
        EndIf
    EndIf
    
    If bln = 2 Then ' если черных = 2 - считаем
        f1 = 0 ' индекс первого заданного (черного) поля
        f2 = 0 ' индекс второго заданного (черного) поля
        For i = 1 To 5 ' обходим все поля
            If fd[i][4] = 0 Then ' определяем, какие чёрные
                If f1 = 0 Then ' и записываем их индексы
                    f1 = i
                Else
                    f2 = i
                EndIf
            EndIf
        EndFor
        
        If f1 = 1 Then ' если катет a
            If f2 = 2 Then ' и катет b
                fd[4][3] = Math.GetDegrees(Math.ArcTan(fd[1][3]/fd[2][3])) ' считаем A
                fd[4][4] = 1 ' синий
                fd[5][3] = 90 - fd[4][3] ' считаем B
                fd[5][4] = 1 ' синий
                fd[3][3] = fd[2][3] / Math.Sin(Math.GetRadians(fd[5][3])) ' считаем c
                fd[3][4] = 1 ' синий
            ElseIf f2 = 3 Then ' и гипотенуза с
                fd[4][3] = Math.GetDegrees(Math.ArcSin(fd[1][3]/fd[3][3])) ' считаем A
                fd[4][4] = 1 ' синий
                fd[5][3] = 90 - fd[4][3] ' считаем B
                fd[5][4] = 1 ' синий
                fd[2][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[5][3]))  ' считаем b
                fd[2][4] = 1 ' синий
            ElseIf f2 = 4 Then ' и угол A
                fd[5][3] = 90 - fd[4][3] ' считаем B
                fd[5][4] = 1 ' синий
                fd[3][3] = fd[1][3] / Math.Sin(Math.GetRadians(fd[4][3])) ' считаем c
                fd[3][4] = 1 ' синий
                fd[2][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[5][3])) ' считаем b
                fd[2][4] = 1 ' синий
            ElseIf f2 = 5 Then ' и угол B
                fd[4][3] = 90 - fd[5][3] ' считаем A
                fd[4][4] = 1 ' синий
                fd[3][3] = fd[1][3] / Math.Sin(Math.GetRadians(fd[4][3])) ' считаем c
                fd[3][4] = 1 ' синий
                fd[2][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[5][3])) ' считаем b
                fd[2][4] = 1 ' синий
            EndIf
        ElseIf f1 = 2 Then ' если катет b
            If f2 = 3 Then ' и гипотенуза с
                fd[5][3] = Math.GetDegrees(Math.ArcSin(fd[2][3]/fd[3][3])) ' считаем B
                fd[5][4] = 1 ' синий
                fd[4][3] = 90 - fd[5][3] ' считаем A
                fd[4][4] = 1 ' синий
                fd[1][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[4][3])) ' считаем a
                fd[1][4] = 1 ' синий
            ElseIf f2 = 4 Then ' и угол A
                fd[5][3] = 90 - fd[4][3] ' считаем B
                fd[5][4] = 1 ' синий
                fd[3][3] = fd[2][3] / Math.Sin(Math.GetRadians(fd[5][3])) ' считаем c
                fd[3][4] = 1 ' синий
                fd[1][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[4][3])) ' считаем a
                fd[1][4] = 1 ' синий
            ElseIf f2 = 5 Then ' и угол B
                fd[4][3] = 90 - fd[5][3] ' считаем A
                fd[4][4] = 1 ' синий
                fd[3][3] = fd[2][3] / Math.Sin(Math.GetRadians(fd[5][3])) ' считаем c
                fd[3][4] = 1 ' синий
                fd[1][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[4][3])) ' считаем a
                fd[1][4] = 1 ' синий
            EndIf
        ElseIf f1 = 3 Then ' если гипотенуза c
            If f2 = 4 Then ' и угол A
                fd[5][3] = 90 - fd[4][3] ' считаем B
                fd[5][4] = 1 ' синий
                fd[1][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[4][3])) ' считаем a
                fd[1][4] = 1 ' синий
                fd[2][3] = fd[3][3] * Math.Cos(Math.GetRadians(fd[4][3])) ' считаем b
                fd[2][4] = 1 ' синий
            ElseIf f2 = 5 Then ' и угол B
                fd[4][3] = 90 - fd[5][3] ' считаем A
                fd[4][4] = 1 ' синий
                fd[2][3] = fd[3][3] * Math.Sin(Math.GetRadians(fd[5][3])) ' считаем b
                fd[2][4] = 1 ' синий
                fd[1][3] = fd[3][3] * Math.Cos(Math.GetRadians(fd[5][3])) ' считаем a
                fd[1][4] = 1 ' синий
            EndIf
        Else ' только два угла - посчитать нельзя!
            If cf = 4 Then ' расчёт второго угла! сумма = 90
                fd[5][3] = 90 - fd[4][3]
                fd[5][4] = 1 ' синий
            Else
                fd[4][3] = 90 - fd[5][3]
                fd[4][4] = 1 ' синий
            EndIf
            For i = 1 To 3 ' обходим все остальные поля
                fd[i][4] = 8 ' сбрасываем их в серые
            EndFor
            fd[cf][4] = 0 ' оставляем чёрным только текущее
        EndIf
    ElseIf bln < 2 Then ' если меньше - ничего не делаем
        ' ничего не делаем
    Else ' если больше 2 - оставляем чёрным только текущее
        For i = 1 To 5 ' обходим все поля
            fd[i][4] = 8 ' сбрасываем их в серые
        EndFor
        fd[cf][4] = 0 ' оставляем чёрным только текущее
    EndIf
    
    ' проход по полям и вывод текущих значений
    For i = 1 To 5
        TextWindow.CursorTop= fd[i][2] ' ставим в строку вывода
        For j=fd[i][1] To fd[i][1]+21 ' заполняем поле пробелами = закрашиваем его фоном
            TextWindow.CursorLeft=j
            TextWindow.Write(" ")
        EndFor
        TextWindow.CursorLeft = fd[i][1] ' в начало строки
        TextWindow.ForegroundColor = fd[i][4] ' цвет
        
        fd[i][3] = LDText.Replace(fd[i][3], "," , ".")
        
        str = fd[i][3]
        If Text.GetLength(fd[i][3])>21 Then ' если очень длинное значение
            fd[i][3] = Text.GetSubText(fd[i][3],1,20)
            str = Text.Append("~",fd[i][3])
        EndIf
        TextWindow.Write(str)
        
        If fd[i][3] = "" Then ' если пустая
            TextWindow.CursorLeft = fd[i][1] ' в начало строки
            TextWindow.ForegroundColor = 8 ' цвет - серый
            TextWindow.Write("0") ' и выводим 0
        EndIf
    EndFor
    
    TextWindow.CursorTop = fd[cf][2] ' возврат курсора в текущее поле
    If Text.GetLength(fd[cf][3]) < 21 Then
        TextWindow.CursorLeft = fd[cf][1] + Text.GetLength(fd[cf][3]) ' возврат курсора в текущее поле - в конец текста
    Else
        TextWindow.CursorLeft = fd[cf][1] + 21
    EndIf
EndSub
'____________________________________________________________________________

'******************************______________________________________________
' начало

cf = 1 ' индекс текущего поля
oldstr = "" ' старое значение поля
oldcl = 8 ' старый цвет поля
EntFl = 0 ' флаг нажатия Enter

For i=0 To 9 ' заполняем массив кодов клавиатуры
    dk[i][1] = "D" + i
    dk[i][2] = "NumPad" + i
EndFor

' Начальное заполнение массива полей
fd[1][1] = 17 ' х
fd[1][2] = 10 ' y

fd[2][1] = 17 ' х
fd[2][2] = 11 ' y

fd[3][1] = 22 ' х
fd[3][2] = 12 ' y

fd[4][1] = 11 ' х
fd[4][2] = 14 ' y

fd[5][1] = 11 ' х
fd[5][2] = 15 ' y

For i=1 To 5
    fd[i][3] = "" ' значение
    fd[i][4] = 8 ' "цвет" (0 - черный, 1 (3,9) - синий, 7,8 - серый)
EndFor

TextWindow.Clear()

TextWindow.CursorLeft = 3 ' рисуем треугольник
TextWindow.CursorTop = 1
TextWindow.Write("+")

For i=2 To 6
    TextWindow.CursorLeft = 3
    TextWindow.CursorTop = i
    TextWindow.Write("|")
EndFor

TextWindow.CursorLeft = 3
TextWindow.CursorTop = 7
TextWindow.Write("+---------------------+")

y=1 ' рисуем гипотенузу
For i=4 To 19 Step 3
    TextWindow.CursorLeft = i
    TextWindow.CursorTop = y
    TextWindow.Write("---")
    y=y+1
EndFor

TextWindow.CursorLeft = 5 ' обозначение прямого угла
TextWindow.CursorTop = 6
TextWindow.Write("|")

TextWindow.CursorLeft = 4
TextWindow.CursorTop = 5
TextWindow.Write("_")

TextWindow.CursorLeft = 1 ' подписи сторон - буквами
TextWindow.CursorTop = 4
TextWindow.Write("a")

TextWindow.CursorLeft = 14
TextWindow.CursorTop = 8
TextWindow.Write("b")

TextWindow.CursorLeft = 16
TextWindow.CursorTop = 3
TextWindow.Write("c")

TextWindow.CursorLeft = 2 ' подписи углов
TextWindow.CursorTop = 8
TextWindow.Write("C")

TextWindow.CursorLeft = 26
TextWindow.CursorTop = 8
TextWindow.Write("A")

TextWindow.CursorLeft = 2
TextWindow.CursorTop = 0
TextWindow.Write("B")

' выводим внизу инфо о параметрах треугольника
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 10
TextWindow.Write("Катет a |BC| = ") 'поле! x=17 y=10
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 11
TextWindow.Write("Катет b |AC| = ") 'поле! x=17 y=11
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 12
TextWindow.Write("Гипотенуза с |AB| = ") 'поле! x=22 y=12
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 14
TextWindow.Write("Угол А = ") 'поле! x=11 y=14
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 15
TextWindow.Write("Угол B = ") 'поле! x=11 y=15
TextWindow.CursorLeft = 2
TextWindow.CursorTop = 16
TextWindow.Write("Угол C = ")

TextWindow.BackgroundColor = 15 ' поле прямого угла
TextWindow.ForegroundColor = 0
TextWindow.Write("90.0                 ")

TextWindow.BackgroundColor = 0 ' название
TextWindow.ForegroundColor = 7
TextWindow.CursorTop = 0
TextWindow.CursorLeft = 20
TextWindow.WriteLine("Калькулятор треугольников v. 2.3 (с) KTsoft 10.11.2021")

TextWindow.CursorTop = 18 ' help
TextWindow.CursorLeft = 0
TextWindow.WriteLine("Расчёт - автоматически после ввода двух значений. Углы - в градусах. Cтрелки - смена поля,")
TextWindow.Write("BS, цифры, точки - редактирование поля, Del - очистка поля, Enter - ввод данных поля, Esc - выход.")

' рисуем поля ввода
TextWindow.BackgroundColor = 15
TextWindow.ForegroundColor = 0
TextWindow.CursorLeft = 17
TextWindow.CursorTop = 10
TextWindow.Write("                     ")
TextWindow.CursorLeft = 17
TextWindow.CursorTop = 11
TextWindow.Write("                     ")
TextWindow.CursorLeft = 22
TextWindow.CursorTop = 12
TextWindow.Write("                     ")
TextWindow.CursorLeft = 11
TextWindow.CursorTop = 14
TextWindow.Write("                     ")
TextWindow.CursorLeft = 11
TextWindow.CursorTop = 15
TextWindow.Write("                     ")

Calc() ' выводим начальные значения

While "True" ' бесконечный цикл
    Program.Delay(100) ' чтоб процессор не грелся
EndWhile

 

Поделиться: