FEAT: Create basic model embed (#588)
* initial commit, issue with OpenSCAD embed viewing * initial implementation * Fix openscad size bug * Add overlays to embed * Remove console.log and reuse exact query Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit was merged in pull request #588.
This commit is contained in:
@@ -5,7 +5,7 @@ datasource db {
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = "native"
|
||||
binaryTargets = ["native", "darwin-arm64", "darwin"]
|
||||
}
|
||||
|
||||
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
|
||||
|
||||
@@ -56,6 +56,7 @@ const Routes = () => {
|
||||
<Route path="/u/{userName}" page={UserPage} name="user" />
|
||||
<Route path="/u/{userName}/{projectTitle}" page={ProjectPage} name="project" />
|
||||
<Route path="/u/{userName}/{projectTitle}/ide" page={IdeProjectPage} name="ide" />
|
||||
<Route path="/u/{userName}/{projectTitle}/embed" page={EmbedProjectPage} name="embed" />
|
||||
<Route path="/u/{userName}/{projectTitle}/social-card" page={SocialCardPage} name="socialCard" />
|
||||
|
||||
<Private unauthenticated="home" role="admin">
|
||||
|
||||
31
app/web/src/components/EmbedProject/EmbedProject.tsx
Normal file
31
app/web/src/components/EmbedProject/EmbedProject.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Seo from 'src/components/Seo/Seo'
|
||||
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
|
||||
import { useIdeState } from 'src/helpers/hooks/useIdeState'
|
||||
import type { Project } from 'src/components/EmbedProjectCell/EmbedProjectCell'
|
||||
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
|
||||
interface Props {
|
||||
project?: Project
|
||||
}
|
||||
|
||||
const EmbedProject = ({ project }: Props) => {
|
||||
const [state, thunkDispatch] = useIdeState()
|
||||
const { viewerDomRef, handleViewerSizeUpdate } = use3dViewerResize()
|
||||
|
||||
useEffect(() => {
|
||||
handleViewerSizeUpdate()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen" ref={viewerDomRef} >
|
||||
<IdeContext.Provider value={{ state, thunkDispatch, project }}>
|
||||
<IdeViewer />
|
||||
</IdeContext.Provider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmbedProject
|
||||
@@ -0,0 +1,6 @@
|
||||
// Define your own mock data here:
|
||||
export const standard = (/* vars, { ctx, req } */) => ({
|
||||
ideProject: {
|
||||
id: 42,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Loading, Empty, Success } from './EmbedProjectCell'
|
||||
import { standard } from './EmbedProjectCell.mock'
|
||||
|
||||
export const loading = () => {
|
||||
return Loading ? <Loading /> : null
|
||||
}
|
||||
|
||||
export const empty = () => {
|
||||
return Empty ? <Empty /> : null
|
||||
}
|
||||
|
||||
export const success = () => {
|
||||
return Success ? <Success {...standard()} /> : null
|
||||
}
|
||||
|
||||
export default { title: 'Cells/IdeProjectCell' }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { render, screen } from '@redwoodjs/testing'
|
||||
import { Loading, Empty, Success } from './EmbedProjectCell'
|
||||
import { standard } from './EmbedProjectCell.mock'
|
||||
|
||||
describe('IdeProjectCell', () => {
|
||||
test('Loading renders successfully', () => {
|
||||
render(<Loading />)
|
||||
// Use screen.debug() to see output
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('Empty renders successfully', async () => {
|
||||
render(<Empty />)
|
||||
expect(screen.getByText('Empty')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
test('Success renders successfully', async () => {
|
||||
render(<Success ideProject={standard().ideProject} />)
|
||||
expect(screen.getByText(/42/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
47
app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx
Normal file
47
app/web/src/components/EmbedProjectCell/EmbedProjectCell.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useIdeState } from 'src/helpers/hooks/useIdeState'
|
||||
import { IdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||
import EmbedViewer from '../EmbedViewer/EmbedViewer'
|
||||
import { QUERY as IdeQuery } from 'src/components/IdeProjectCell'
|
||||
|
||||
export const QUERY = IdeQuery
|
||||
export interface Project {
|
||||
id: string
|
||||
title: string
|
||||
code: string
|
||||
description: string
|
||||
mainImage: string
|
||||
createdAt: string
|
||||
cadPackage: 'openscad' | 'cadquery'
|
||||
user: {
|
||||
id: string
|
||||
userName: string
|
||||
image: string
|
||||
}
|
||||
}
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>Project not found</div>
|
||||
|
||||
interface SaveCodeArgs {
|
||||
input: any
|
||||
id: string
|
||||
isFork: boolean
|
||||
}
|
||||
|
||||
export const Success = ({
|
||||
project,
|
||||
refetch,
|
||||
}: {
|
||||
project: Project
|
||||
refetch: any
|
||||
}) => {
|
||||
const [state, thunkDispatch] = useIdeState()
|
||||
|
||||
|
||||
return (
|
||||
<IdeContext.Provider value={{ state, thunkDispatch, project }}>
|
||||
<EmbedViewer project={project} />
|
||||
</IdeContext.Provider>
|
||||
)
|
||||
}
|
||||
34
app/web/src/components/EmbedViewer/EmbedViewer.tsx
Normal file
34
app/web/src/components/EmbedViewer/EmbedViewer.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useIdeInit } from 'src/components/EncodedUrl/helpers'
|
||||
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
|
||||
import IdeViewer from 'src/components/IdeViewer/IdeViewer'
|
||||
import { use3dViewerResize } from 'src/helpers/hooks/use3dViewerResize'
|
||||
import CadPackage from '../CadPackage/CadPackage'
|
||||
import LogoType from '../LogoType/LogoType'
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
function EmbedViewer() {
|
||||
const { state, project } = useIdeContext()
|
||||
useIdeInit(project?.cadPackage, project?.code || state?.code, "viewer")
|
||||
const { viewerDomRef } = use3dViewerResize()
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col h-screen group" ref={viewerDomRef}>
|
||||
<IdeViewer isMinimal={true} />
|
||||
<div className="absolute top-5 left-5 text-ch-gray-300">
|
||||
<h1 className="mb-4 text-4xl font-normal capitalize ">
|
||||
{project?.title.replace(/-/g, ' ')}
|
||||
</h1>
|
||||
<h2 className="mb-2 transition-opacity duration-100 group-hover:opacity-0">by @{ project?.user?.userName }</h2>
|
||||
<h2 className="transition-opacity duration-100 group-hover:opacity-0">built with <div className="inline-block"><CadPackage cadPackage={project?.cadPackage} className="px-3 py-2"/></div></h2>
|
||||
</div>
|
||||
<div className="absolute grid items-center grid-flow-col-dense gap-2 bottom-5 right-5 text-ch-gray-300">
|
||||
View on <Link className="inline-block" to={routes.project({
|
||||
userName: project?.user?.userName,
|
||||
projectTitle: project?.title.toString(),
|
||||
})}><LogoType className="inline-block" wrappedInLink={true}/></Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmbedViewer
|
||||
@@ -4,8 +4,10 @@ import { PureIdeViewer } from './PureIdeViewer'
|
||||
|
||||
const IdeViewer = ({
|
||||
handleOwnCamera = false,
|
||||
isMinimal = false,
|
||||
}: {
|
||||
handleOwnCamera?: boolean
|
||||
handleOwnCamera?: boolean,
|
||||
isMinimal?: boolean,
|
||||
}) => {
|
||||
const { state, thunkDispatch } = useIdeContext()
|
||||
const dataType = state.objectData?.type
|
||||
@@ -51,6 +53,7 @@ const IdeViewer = ({
|
||||
isLoading={state.isLoading}
|
||||
camera={state?.camera}
|
||||
ideType={ideType}
|
||||
isMinimal={isMinimal}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ export function PureIdeViewer({
|
||||
alt="code-cad preview"
|
||||
id="special"
|
||||
src={URL.createObjectURL(image)}
|
||||
className="h-full w-full"
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
43
app/web/src/components/LogoType/LogoType.js
Normal file
43
app/web/src/components/LogoType/LogoType.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import Tooltip from '@material-ui/core/Tooltip'
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
import Svg from 'src/components/Svg'
|
||||
|
||||
export default function LogoType({ className="", wrappedInLink=false }) {
|
||||
return (
|
||||
<ul className={"flex items-center " + className}>
|
||||
<li>
|
||||
{ (wrappedInLink
|
||||
? <Link to={routes.home()}>
|
||||
<div className="ml-2 overflow-hidden rounded-full">
|
||||
<Svg className="w-10" name="favicon" />
|
||||
</div>
|
||||
</Link>
|
||||
: <div>
|
||||
<div className="ml-2 overflow-hidden rounded-full">
|
||||
<Svg className="w-10" name="favicon" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<Tooltip title="Very alpha, there's lots of work todo">
|
||||
<div className="flex ml-4">
|
||||
{/* Because of how specific these styles are to this heading/logo and it doesn't need to be replicated else where as well as it's very precise with the placement of "pre-alpha" I think it's appropriate. */}
|
||||
<h2
|
||||
className="py-1 text-2xl text-indigo-300 md:text-5xl font-ropa-sans md:tracking-wider"
|
||||
style={{ letterSpacing: '0.3em' }}
|
||||
>
|
||||
CadHub
|
||||
</h2>
|
||||
<div
|
||||
className="hidden text-sm font-bold text-pink-400 font-ropa-sans md:block"
|
||||
style={{ paddingBottom: '2rem', marginLeft: '-1.8rem' }}
|
||||
>
|
||||
pre-alpha
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -53,6 +53,7 @@ const ProjectProfile = ({
|
||||
})
|
||||
)
|
||||
}, [currentUser, project?.title, userProject.userName])
|
||||
console.log('from ProjectProfile', { cadPackage: project.cadPackage, code: project.code })
|
||||
useIdeInit(project?.cadPackage, project?.code, 'viewer')
|
||||
const [newDescription, setNewDescription] = useState(project?.description)
|
||||
const onDescriptionChange = (description) => setNewDescription(description())
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useState, useEffect } from 'react'
|
||||
import { Link, routes, navigate } from '@redwoodjs/router'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import { Toaster, toast } from '@redwoodjs/web/toast'
|
||||
import Tooltip from '@material-ui/core/Tooltip'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { getActiveClasses } from 'get-active-classes'
|
||||
import Footer from 'src/components/Footer'
|
||||
@@ -12,11 +11,11 @@ import NavPlusButton from 'src/components/NavPlusButton'
|
||||
import ReactGA from 'react-ga'
|
||||
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||
|
||||
import Svg from 'src/components/Svg'
|
||||
import { ImageFallback } from 'src/components/ImageUploader'
|
||||
import useUser from 'src/helpers/hooks/useUser'
|
||||
import './MainLayout.css'
|
||||
import RecentProjectsCell from 'src/components/RecentProjectsCell'
|
||||
import LogoType from 'src/components/LogoType'
|
||||
|
||||
let previousSubmission = ''
|
||||
|
||||
@@ -72,39 +71,12 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
|
||||
}, [hash, client])
|
||||
return (
|
||||
<div
|
||||
className="h-full flex flex-col ch-scrollbar overflow-y-scroll overflow-x-hidden"
|
||||
className="flex flex-col h-full overflow-x-hidden overflow-y-scroll ch-scrollbar"
|
||||
style={{ perspective: '1px', perspectiveOrigin: 'top center' }}
|
||||
>
|
||||
<header id="cadhub-main-header">
|
||||
<nav className="flex justify-between h-16 sm:px-4 bg-ch-gray-900">
|
||||
<ul className="flex items-center">
|
||||
<li>
|
||||
<Link to={routes.home()}>
|
||||
<div className="rounded-full overflow-hidden ml-2">
|
||||
<Svg className="w-10" name="favicon" />
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Tooltip title="Very alpha, there's lots of work todo">
|
||||
<div className="ml-4 flex">
|
||||
{/* Because of how specific these styles are to this heading/logo and it doesn't need to be replicated else where as well as it's very precise with the placement of "pre-alpha" I think it's appropriate. */}
|
||||
<h2
|
||||
className="text-indigo-300 text-2xl md:text-5xl font-ropa-sans py-1 md:tracking-wider"
|
||||
style={{ letterSpacing: '0.3em' }}
|
||||
>
|
||||
CadHub
|
||||
</h2>
|
||||
<div
|
||||
className="text-pink-400 text-sm font-bold font-ropa-sans hidden md:block"
|
||||
style={{ paddingBottom: '2rem', marginLeft: '-1.8rem' }}
|
||||
>
|
||||
pre-alpha
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
<LogoType />
|
||||
<ul className="flex items-center">
|
||||
<li
|
||||
className={getActiveClasses(
|
||||
@@ -114,29 +86,29 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
|
||||
<NavPlusButton />
|
||||
</li>
|
||||
{isAuthenticated ? (
|
||||
<li className="h-10 w-10">
|
||||
<Popover className="relative outline-none w-full h-full">
|
||||
<li className="w-10 h-10">
|
||||
<Popover className="relative w-full h-full outline-none">
|
||||
<Popover.Button
|
||||
disabled={!isAuthenticated || !currentUser}
|
||||
className="h-full w-full outline-none border-ch-gray-400 border-2 rounded-full"
|
||||
className="w-full h-full border-2 rounded-full outline-none border-ch-gray-400"
|
||||
>
|
||||
{!loading && (
|
||||
<ImageFallback
|
||||
width={80}
|
||||
className="rounded-full object-cover"
|
||||
className="object-cover rounded-full"
|
||||
imageId={user?.image}
|
||||
/>
|
||||
)}
|
||||
</Popover.Button>
|
||||
{currentUser && (
|
||||
<Popover.Panel className="w-48 absolute z-10 right-0 bg-ch-gray-700 mt-4 px-3 py-2 rounded shadow-md overflow-hidden text-ch-gray-300">
|
||||
<Popover.Panel className="absolute right-0 z-10 w-48 px-3 py-2 mt-4 overflow-hidden rounded shadow-md bg-ch-gray-700 text-ch-gray-300">
|
||||
<Link to={routes.user({ userName: user?.userName })}>
|
||||
<p className="my-2 text-ch-blue-400 font-fira-code leading-4 text-sm">
|
||||
<p className="my-2 text-sm leading-4 text-ch-blue-400 font-fira-code">
|
||||
Hello {user?.name}
|
||||
</p>
|
||||
</Link>
|
||||
<Link
|
||||
className="my-2 block hover:text-ch-pink-300"
|
||||
className="block my-2 hover:text-ch-pink-300"
|
||||
to={routes.user({ userName: user?.userName })}
|
||||
>
|
||||
<div>View Your Profile</div>
|
||||
@@ -149,7 +121,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
|
||||
Logout
|
||||
</a>
|
||||
<hr className="my-4" />
|
||||
<p className="text-ch-blue-400 font-fira-code leading-4 text-sm">
|
||||
<p className="text-sm leading-4 text-ch-blue-400 font-fira-code">
|
||||
Recent Projects
|
||||
</p>
|
||||
<RecentProjectsCell userName={user?.userName} />
|
||||
@@ -161,7 +133,7 @@ const MainLayout = ({ children, shouldRemoveFooterInIde }) => {
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-ch-gray-300 mr-1 sm:mr-2 px-2 sm:px-4 py-2 border-2 border-ch-gray-400 rounded-full hover:bg-ch-gray-600"
|
||||
className="px-2 py-2 mr-1 border-2 rounded-full text-ch-gray-300 sm:mr-2 sm:px-4 border-ch-gray-400 hover:bg-ch-gray-600"
|
||||
onClick={recordedLogin}
|
||||
>
|
||||
Sign In/Up
|
||||
|
||||
11
app/web/src/pages/EmbedProjectPage/EmbedProjectPage.test.tsx
Normal file
11
app/web/src/pages/EmbedProjectPage/EmbedProjectPage.test.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import EmbedProjectPage from './EmbedProjectPage'
|
||||
|
||||
describe('EmbedProjectPage', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<EmbedProjectPage />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
11
app/web/src/pages/EmbedProjectPage/EmbedProjectPage.tsx
Normal file
11
app/web/src/pages/EmbedProjectPage/EmbedProjectPage.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import EmbedProjectCell from 'src/components/EmbedProjectCell'
|
||||
|
||||
const EmbedProjectPage = ({ userName, projectTitle }) => {
|
||||
return (
|
||||
<>
|
||||
<EmbedProjectCell userName={userName} projectTitle={projectTitle} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default EmbedProjectPage
|
||||
43
docs/blog/2021-08-14-ux-studies-timeline.mdx
Normal file
43
docs/blog/2021-08-14-ux-studies-timeline.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
slug: ux-studies-intro
|
||||
title: "GUI-CAD UX studies: introduction"
|
||||
author: Frank Noirot
|
||||
author_title: CadHub Core Team
|
||||
author_url: https://github.com/franknoirot
|
||||
author_image_url: https://avatars.githubusercontent.com/u/23481541?v=4
|
||||
tags: []
|
||||
---
|
||||
|
||||
import Image from '@theme/IdealImage';
|
||||
|
||||
import ivanSutherland from '../static/img/blog/ux-case-studies-intro/ivan-sutherland-sketchpad.jpg';
|
||||
|
||||
I'm helping CadHub out by designing the interfaces for the [new editor](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/CadHub?node-id=1114%3A1608), [project viewer](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/?node-id=1046%3A0), and [more](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/?node-id=1150%3A1618). Right now we're focused on getting the Code-CAD user experience perfected so that users can try out all the great Code-CAD packages out there in a simple and sharable way. But we think that the future of Code-CAD will pull UX lessons from traditional, GUI-based CAD systems. So I'll be taking a look at the history and UX of some of today's CAD tools to see how we might bring them along with the Code-CAD evolution.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
## Why GUIs aren't enough anymore
|
||||
|
||||
As others like [Jessie Frazelle](https://medium.com/embedded-ventures/mechanical-cad-yesterday-today-and-tomorrow-981cef7e06b1) have pointed out, the history of CAD software has been focused primarily on using software to emulate the frictionless user experiences of sketching and modelling by hand. That paradigm lead most of the major tools to build GUI-based systems, as they correctly assumed at the the time that the GUI offered an interface that could be understood by people in the industry. Decades have passed and the same assumption still forms the foundation of the paradigm, but could these assumptions have fundamentally changed?
|
||||
|
||||
<Image img={ivanSutherland} alt="Ivan Sutherland's Sketchpad program from the 1960's, a man using a pen-like tool on a screen to manipulate a 2D model, considered the first CAD program." className="mb-8 bg-contain rounded-md overflow-hidden max-w-lg mx-auto" />
|
||||
|
||||
It's hard to understate how much of a sea change web development has brought to technical culture. In the past decade the web technologies of HTML, CSS, and especially JavaScript have trained a large part of technical workers to think not in terms of software packages, but in terms of the technologies and languages that are used to construct them, because as a culture we have become accustomed to the idea that there is always an API powering whatever tool we're using. Technical users of course still want seamless GUI user experiences on platforms, but increasingly they also want the ability to get under the hood and use the APIs that power whatever tool or platform they're on. This trend is evident in the rise of API-first services like [Stripe](https://stripe.com) and monolith-fracturing trends like [JAMstack web development](https://jamstack.org).
|
||||
|
||||
With Code-CAD, we are putting a spotlight on this sea change in user expectations, and putting out a call to action for people to start creating experiences for this web-native, language-comfortable audience of CAD users. With CadHub, we're building a showcase for the great Code-CAD packages like [CadQuery](https://cadquery.readthedocs.io/en/latest/) and [OpenSCAD](https://openscad.org/) that have been under development by early adopters for years.
|
||||
|
||||
## A Gooey Hegelian Dialectic
|
||||
|
||||
Okay, so we in the Code-CAD community are a bunch of developers who want more interfaces from our CAD programs. We're comfortable with programming languages and APIs and we want access to them in addition to the GUIs that CAD has traditionally provided. That's all great, but then why isn't Code-CAD mainstream right now?
|
||||
|
||||
For one, the process of building a robust, text-first approach to so visual an activity is, as [Kurt has written about](/blog/right-level-of-abstraction) on this blog, incredibly difficult. But the Code-CAD community has been doing the monumental work of developing clean, expressive APIs for modelling. That important work is what we want to showcase and make more accessible with CadHub. But we think it's only half of the equation for creating the next step in CAD.
|
||||
|
||||
There are myriads of thorny user experience problems that have been solved by the dominant CAD packages of today. They are amazing pieces of software that users know and love. And all those clever UX solutions were created in the design space of GUIs. If Code-CAD as a paradigm is going to become the new normal for computer-aided design, we need to understand and address all the innovation that GUI-CAD has brought to design, and translate them into Code-CAD. We need to find a gooey-code synthesis. As previously stated, this new generation of users *still want seamless GUI experiences*. Code-CAD needs to provide a way of switching seamlessly between "Application Programming" and "Graphical User" interfaces.
|
||||
|
||||
## Already under construction
|
||||
|
||||
I'll try to explore that design space with a few brief case studies on UX that I love from existing CAD and 3D modelling software. I'm looking for key experiences that help empower designers, how they operate in the GUI-CAD paradigm, and how Code-CAD might provide code-based synonyms of these GUI experiences.
|
||||
|
||||
But I want to mention that work is already being done on this front. Jeremy Wright of the CadQuery team is building [Semblage](https://semblage.7bindustries.com/en/latest/), a GUI-code hybrid built with CadQuery and the Godot gaming engine. [BuildBee](https://makecode.buildbee.com/) lets users switch between Scratch-like block interface and JavaScript code for making models. Blender provides an [excellent Python API](https://docs.blender.org/api/current/index.html) for almost all of its incredible functionality, and there a dozen other projects pushing things forward while we look to the present and the past for more inspiration.
|
||||
|
||||
Our first stop will be the timeline feature of AutoDesk Fusion360, which is a clever way to make the order of operations in modelling intuitive. Stay tuned for this post and more in the coming weeks, check out our work on [GitHub](https://github.com/Irev-Dev/cadhub/discussions/404) and [Figma](https://www.figma.com/file/VUh53RdncjZ7NuFYj0RGB9/?node-id=633%3A0), sign up for [Kurt's newsletter](https://kurthutten.com/), and join [our Discord](https://discord.gg/sFYJyEJ6) to get plugged into our ongoing discussions about the future of Code-CAD.
|
||||
Reference in New Issue
Block a user